Appearance
0017 — Small Groups & Ministries management ​
Status: Accepted (2026-06-18 — owner accepted during ADR review)
Date: 2026-06-17
ADO work item: AB#3092
Deciders: Kristopher Turner (platform owner)
Context ​
Heritage Virginia organizes members around two overlapping structures: ministries (standing, leader-driven programs such as worship, youth, or outreach) and small groups (recurring fellowship circles, often home-based or class-based). Both structures share a common shape: a named group, a defined membership roster, and one or more designated leaders whose authority is explicitly bounded to that group.
ADR 0006 (two-plane RBAC) established five canonical platform roles, two of which — Ministry Leader and Small Group Leader — exist precisely to represent this kind of scoped authority. That ADR names the roles but intentionally defers the definition of what a "group" or "ministry" is as a data entity to a feature ADR. This is that ADR.
ADR 0008 (platform composition) establishes that every feature is a slice of the API-first platform core. Group and ministry data must therefore live in Azure SQL (ADR 0004) behind the apps/api backend, accessible to clients only through the typed api-client SDK — the same rule that applies to every other domain.
The urgency of this decision is not incidental: groups and ministries are a platform-level scoping anchor, not a self-contained feature. The Calendar feature (ADR 0011), Announcements feature (ADR 0012), and the Messaging feature all need a shared, authoritative group-membership relationship to scope visibility, targeting, and delivery. If groups are not modeled as first-class entities before those features are built, each feature will invent its own group concept, producing type drift, divergent rosters, and inconsistent RBAC enforcement. The platform strategy (Epic AB#3076, Feature AB#3092) calls this out explicitly as a prerequisite for those downstream features.
This is a planning/architecture decision. It introduces no code; it defines the data model and authority boundaries that Phase 1 implementation and every dependent feature must honor.
Decision ​
We will model small groups and ministries as first-class platform entities — stored in Azure SQL behind the
apps/apibackend, each with a stable identity, a membership roster, and one or more assigned leaders — so that the Ministry Leader and Small Group Leader roles from ADR 0006 resolve to concrete group-scoped authority, and so that Calendar, Announcements, and Messaging can scope their behavior against a single, authoritative group-membership relationship rather than maintaining their own.
Data shape ​
The model introduces three tables in the existing Azure SQL database:
text
Groups
id (PK)
type ENUM('ministry', 'small_group')
name
description
is_active
created_at
updated_at
GroupMemberships
id (PK)
group_id FK → Groups.id
user_id FK → Users.id
role ENUM('leader', 'member')
joined_at
GroupInvitations (optional Phase 2 extension)
id, group_id, invited_by, email_or_user_id, status, expires_atA single Groups table with a type discriminator unifies ministries and small groups. The shared shape is intentional: the platform treats both as "a named group of members with designated leaders." Feature-specific metadata (e.g., meeting location, ministry category) is an extension for Phase 2.
GroupMemberships.role carries the leader / member distinction; the platform RBAC layer maps a leader membership to the Ministry Leader or Small Group Leader role claim (per ADR 0006) scoped to that group_id. A user may hold leader status in one group and member status in another simultaneously.
API surface ​
The feature slice lives at apps/api/src/features/groups/. It exposes:
GET /groups— paginated list; scoped by caller's memberships unless the caller holds an admin or minister role.GET /groups/:id— group detail including roster (leader-or-admin only for full roster; members see summary).POST /groups— create a group (admin or minister only).PATCH /groups/:id— update name, description, active status (admin, minister, or the group's own leader).GET /groups/:id/members— roster (leader-or-admin only).POST /groups/:id/members— add a member (leader-or-admin).DELETE /groups/:id/members/:userId— remove a member (leader-or-admin).PATCH /groups/:id/members/:userId— promote/demote leader flag (admin or minister only, to prevent self-elevation).
All endpoints enforce server-side RBAC per ADR 0006. No client makes authorization decisions of record.
RBAC scoping contract ​
This feature establishes the group-scoped authority contract other features rely on:
- A user with
GroupMemberships.role = 'leader'for a givengroup_idis authorized by the API to manage that group's roster, post group-scoped announcements (ADR 0012), and create group-scoped calendar events (ADR 0011). - The Calendar and Announcements features query
GroupMembershipsto resolve audience membership at render/delivery time — they do not maintain their own group lists. - Messaging targeting (a later feature) follows the same pattern:
group_idas a targeting selector resolved server-side againstGroupMemberships.
This contract is the primary reason groups must be first-class entities rather than tags or per-feature lists.
Alternatives considered ​
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| First-class Groups entity, shared roster (chosen) | Single source of truth for membership; RBAC scoping is clean and consistent; Calendar/Announcements/Messaging all resolve against one table; aligns with ADR 0006 roles | Requires the Groups feature to ship before dependent features can scope against it | — chosen |
| Ad-hoc tags only (no roster, no leader authority) | Zero schema work; ultra-flexible grouping | No clean leader authority boundary; RBAC cannot scope to a tag; no canonical membership for Calendar/Announcements to target; rejected at ADR 0006 when the Leader roles were defined | Rejected — fundamentally incompatible with server-side RBAC and the scoping requirements of downstream features |
| Third-party church-management SaaS for groups (e.g. Church Community Builder, Planning Center) | Mature group/roster UX out of the box | Breaks the single-platform model; member data leaves the platform; RBAC cannot enforce group scope across an external boundary; adds cost; violates the "closed community, one platform" principle from the platform strategy | Rejected — incompatible with ADR 0008 (API-first, data in Azure SQL) and the cost/control constraints |
| Per-feature duplicate group lists (each feature maintains its own concept of "group") | Features ship independently without a shared dependency | Rosters drift; RBAC must be re-implemented per feature; no canonical truth for who belongs to a group; produces exactly the fragmentation this decision is meant to prevent | Rejected — contradicts the platform composition rules in ADR 0008 and the scoping anchor requirement |
Consequences ​
Positive ​
- The Ministry Leader and Small Group Leader roles from ADR 0006 resolve to a concrete, enforceable boundary: authority is scoped to the specific
group_idfor which the user holds aleadermembership. - Calendar, Announcements, and Messaging can scope visibility and targeting against
GroupMembershipswithout maintaining their own group concept — one table is the answer for all of them. - A minister or admin can see the full cross-group membership picture; leaders see only their own roster. The server enforces this without client cooperation.
- The
typediscriminator keeps the schema minimal now while leaving room to diverge ministries and small groups in Phase 2 if their metadata requirements genuinely differ.
Negative / trade-offs ​
- The Groups feature becomes a build prerequisite for Calendar, Announcements, and Messaging. Any delay in delivering the Groups API delays those features' audience-scoping capability. Mitigation: keep the initial Groups API minimal (the eight endpoints above); defer group invitations and rich metadata to Phase 2.
- The
leader/memberrole enum inGroupMembershipsis deliberately narrow. If a ministry later needs sub-roles (e.g., "section leader" within a worship ministry), the schema must be extended. Mitigation: document the extension point now; do not add sub-roles speculatively. - Leaders cannot promote other members to leader status without admin or minister involvement (by design, to prevent self-elevation). This adds friction for large groups. Mitigation: the minister or admin delegation path is straightforward; the security benefit outweighs the friction.
Risks ​
- Orphaned memberships — if a
Usersrecord is deactivated or a family group is dissolved (ADR 0007),GroupMembershipsrows may reference inactive users. Mitigation: theis_activeflag onGroupsand a soft-delete pattern onGroupMemberships(with aleft_attimestamp) allow the API to filter without data loss; enforce via foreign-key constraints and API-layer validation. - Group proliferation — without governance, the group list becomes unwieldy. Mitigation: only admins and ministers can create groups; the
is_activeflag allows archiving without deletion; an admin roster view is part of the initial API surface. - Dependency inversion — Calendar or Announcements might ship before Groups and work around the missing dependency. Mitigation: the per-feature ADRs for Calendar (0011) and Announcements (0012) explicitly reference this ADR as a prerequisite; the platform strategy build order enforces Groups before those features.
References ​
- Platform strategy — Small Groups & Ministries Management
- ADR 0006 — Two-plane RBAC
- ADR 0007 — Account & Family-Group identity
- ADR 0008 — Platform composition
- ADR 0011 — Community Calendar (forthcoming)
- ADR 0012 — Announcements & one-way broadcast (forthcoming)
- ADO: Epic AB#3076 (Small Groups & Ministries), Feature AB#3092 (this ADR and implementation scope).