31 KiB
Groups - Technical Architecture
Project: Mila - Membership Management System
Feature: Groups Management
Version: 1.0
Last Updated: 2025-01-XX
Status: Architecture Design - Ready for Implementation
Purpose
This document defines the technical architecture for the Groups feature. It focuses on architectural decisions, patterns, module structure, and integration points without concrete implementation details.
Related Documents:
- database-schema-readme.md - Database documentation
- roles-and-permissions-architecture.md - Authorization system
Table of Contents
- Architecture Principles
- Domain Structure
- Data Architecture
- Business Logic Architecture
- UI/UX Architecture
- Integration Points
- Authorization
- Performance Considerations
- Future Extensibility
- Implementation Phases
Architecture Principles
Core Design Decisions
-
Many-to-Many Relationship:
- Members can belong to multiple groups
- Groups can contain multiple members
- Implemented via join table (
member_groups) as separate Ash resource
-
Flat Structure (MVP):
- Groups are initially flat (no hierarchy)
- Architecture designed to allow hierarchical extension later
- No parent/child relationships in MVP
-
Minimal Attributes (MVP):
name,description, andslugin initial versionslugis automatically generated fromname(immutable, URL-friendly)- Extensible for future attributes (dates, status, etc.)
-
Cascade Deletion:
- Deleting a group removes all member-group associations
- Members themselves are not deleted (CASCADE on join table only)
- Requires explicit confirmation with group name input
-
Search Integration:
- Groups searchable within member search (not separate search)
- Group names included in member search vector for full-text search
Domain Structure
Ash Domain: Mv.Membership
Purpose: Groups are part of the Membership domain, alongside Members and CustomFields
New Resources:
Group- Group definitions (name, description, slug)MemberGroup- Join table for many-to-many relationship between Members and Groups
Extended Resources:
Member- Extended withhas_many :groupsrelationship (through MemberGroup)
Module Organization
lib/
├── membership/
│ ├── membership.ex # Domain definition (extended)
│ ├── group.ex # Group resource
│ ├── member_group.ex # MemberGroup join table resource
│ └── member.ex # Extended with groups relationship
├── mv_web/
│ └── live/
│ ├── group_live/
│ │ ├── index.ex # Groups management page
│ │ ├── form.ex # Create/edit group form
│ │ └── show.ex # Group detail view
│ └── member_live/
│ ├── index.ex # Extended with group filtering/sorting
│ └── show.ex # Extended with group display
└── mv/
└── membership/
└── group/ # Future: Group-specific business logic
└── helpers.ex # Group-related helper functions
Data Architecture
Database Schema
groups Table
Attributes:
id- UUID v7 primary keyname- Unique group name (required, max 100 chars)slug- URL-friendly identifier (required, max 100 chars, auto-generated from name)description- Optional description (max 500 chars)inserted_at/updated_at- Timestamps
Constraints:
namemust be unique (case-insensitive, using LOWER(name))slugmust be unique (case-sensitive, exact match)namecannot be nullslugcannot be nullnamemax length: 100 charactersslugmax length: 100 charactersdescriptionmax length: 500 characters
member_groups Table (Join Table)
Attributes:
id- UUID v7 primary keymember_id- Foreign key to members (CASCADE delete)group_id- Foreign key to groups (CASCADE delete)inserted_at/updated_at- Timestamps
Constraints:
- Unique constraint on
(member_id, group_id)- prevents duplicate memberships - CASCADE delete: Removing member removes all group associations
- CASCADE delete: Removing group removes all member associations
Indexes:
- Index on
member_idfor efficient member → groups queries - Index on
group_idfor efficient group → members queries
Ash Resources
Mv.Membership.Group
Relationships:
has_many :member_groups- Relationship to MemberGroup join tablemany_to_many :members- Relationship to Members through MemberGroup
Calculations:
member_count- Integer calculation counting associated members
Actions:
create- Create new group (auto-generates slug from name)read- List/search groups (can query by slug via identity)update- Update group name/description (slug remains unchanged)destroy- Delete group (with confirmation)
Validations:
namerequired, unique (case-insensitive), max 100 charsslugrequired, unique (case-sensitive), max 100 chars, auto-generated, immutabledescriptionoptional, max 500 chars
Identities:
unique_slug- Unique identity onslugfor efficient lookups
Mv.Membership.MemberGroup
Relationships:
belongs_to :member- Relationship to Memberbelongs_to :group- Relationship to Group
Actions:
create- Add member to groupread- Query member-group associationsdestroy- Remove member from group
Validations:
- Unique constraint on
(member_id, group_id)
Mv.Membership.Member (Extended)
New Relationships:
has_many :member_groups- Relationship to MemberGroup join tablemany_to_many :groups- Relationship to Groups through MemberGroup
New Actions:
add_to_groups- Add member to one or more groupsremove_from_groups- Remove member from one or more groups
Business Logic Architecture
Group Management
Create Group:
- Validate name uniqueness
- Automatically generate slug from name (using
GenerateSlugchange, same pattern as CustomFields) - Validate slug uniqueness
- Return created group
Update Group:
- Validate name uniqueness (if name changed)
- Update description
- Slug remains unchanged (immutable after creation)
- Return updated group
Delete Group:
- Check if group has members (for warning display)
- Require explicit confirmation (group name input)
- Cascade delete all
member_groupsassociations - Group itself deleted
Member-Group Association
Add Member to Group:
- Validate member exists
- Validate group exists
- Check for duplicate association
- Create
MemberGrouprecord
Remove Member from Group:
- Find
MemberGrouprecord - Delete association
- Member and group remain intact
Bulk Operations:
- Add member to multiple groups in single transaction
- Remove member from multiple groups in single transaction
Search Integration
Member Search Enhancement:
- Include group names in member search vector
- When searching for member, also search in associated group names
- Example: Searching for a group name finds all members in groups with that name
Implementation:
- Extend
member.search_vectortrigger to include group names - Update trigger on
member_groupschanges - Use PostgreSQL
tsvectorfor full-text search
UI/UX Architecture
Groups Management Page (/groups)
Route: /groups - Groups management index page
Features:
- List all groups in table
- Create new group button
- Edit group (inline or modal)
- Delete group with confirmation modal
- Show member count per group
Table Columns:
- Name (sortable, searchable)
- Description
- Member Count
- Actions (Edit, Delete)
Delete Confirmation Modal:
- Warning: "X members are in this group"
- Confirmation: "All member-group associations will be permanently deleted"
- Input field: Enter group name to confirm
- Delete button disabled until name matches
- Cancel button
Member Overview Integration
New Column: "Groups"
- Display group badges for each member
- Badge shows group name
- Multiple badges if member in multiple groups
- (Optional) Click badge to filter by that group (enhanced UX, can be added later)
Filtering:
- Dropdown/select to filter by group
- "All groups" option (default)
- Filter persists in URL query params
- Works with existing search/sort
Sorting:
- Sort by group name (members with groups first, then alphabetically)
- Sort by number of groups (members with most groups first)
Search:
- Group names included in member search
- Searching group name shows all members in that group
Member Detail View Integration
New Section: "Groups"
- List all groups member belongs to
- Display as badges or list
- Add/remove groups inline
- Link to group detail page
Group Detail View (/groups/:id)
Route: /groups/:id - Group detail page (uses UUID, slug can be used for future /groups/:slug routes)
Features:
- Display group name and description
- List all members in group
- Link to member detail pages
- Edit group button
- Delete group button (with confirmation)
Note: Currently uses UUID for routing. Slug is available for future URL-friendly routes (/groups/:slug).
Accessibility (A11y) Considerations
Requirements:
- All UI elements must be keyboard accessible
- Screen readers must be able to navigate and understand the interface
- ARIA labels and roles must be properly set
Group Badges in Member Overview:
- Badges must have
role="status"and appropriatearia-labelattributes - Badge title should indicate group membership
Clickable Group Badge (for filtering) - Optional:
Note: This is an optional enhancement. The dropdown filter provides the same functionality. The clickable badge improves UX by showing the active filter visually and allowing quick removal.
Estimated effort: 1.5-2.5 hours
- Clickable badges must be proper button elements with
type="button" - Must include
aria-labeldescribing the filter action - Icon for removal should have
aria-hidden="true"
Group Filter Dropdown:
- Select element must have appropriate
id,name, andaria-labelattributes - Options should clearly indicate selected state
Screen Reader Announcements:
- Use
role="status"witharia-live="polite"for dynamic content - Announce filter changes and member count updates
Delete Confirmation Modal:
- Modal must use proper
role="dialog"witharia-labelledbyandaria-describedby - Warning messages must be clearly associated with the modal description
- Form inputs must be properly labeled
Keyboard Navigation:
- All interactive elements (buttons, links, form inputs) must be focusable via Tab key
- Modal dialogs must trap focus (Tab key cycles within modal)
- Escape key closes modals
- Enter/Space activates buttons when focused
Integration Points
Member Search Vector
Trigger Update:
- When
member_groupsrecord created/deleted - Update
members.search_vectorto include group names - Use PostgreSQL trigger for automatic updates
Search Query:
- Extend existing
fuzzy_searchto include group names - Group names added with weight 'B' (same as city, etc.)
Member Form
Future Enhancement:
- Add groups selection in member form
- Multi-select dropdown for groups
- Add/remove groups during member creation/edit
Authorization Integration
Current (MVP):
- Only admins can manage groups
- Uses existing
Mv.Authorization.Checks.HasPermission - Permission:
groupsresource with:allscope
Future:
- Group-specific permissions
- Role-based group management
- Member-level group assignment permissions
Authorization
Permission Model (MVP)
Resource: groups
Actions:
read- View groups (all users with member read permission)create- Create groups (admin only)update- Edit groups (admin only)destroy- Delete groups (admin only)
Scopes:
:all- All groups (for all permission sets that have read access)
Permission Sets Update
Admin Permission Set:
readaction onGroupresource with:allscope - grantedcreateaction onGroupresource with:allscope - grantedupdateaction onGroupresource with:allscope - granteddestroyaction onGroupresource with:allscope - granted
Read-Only Permission Set:
readaction onGroupresource with:allscope - granted
Normal User Permission Set:
readaction onGroupresource with:allscope - granted
Own Data Permission Set:
readaction onGroupresource with:allscope - granted
Note: All permission sets use :all scope for groups. Groups are considered public information that all users with member read permission can view. Only admins can manage (create/update/destroy) groups.
Member-Group Association Permissions
Current (MVP):
- Adding/removing members from groups requires group update permission
- Managed through group edit interface
Future:
- Separate permission for member-group management
- Member-level permissions for self-assignment
Performance Considerations
Database Indexes
Critical Indexes:
groups.name- For uniqueness and search (case-insensitive via LOWER)groups.slug- For uniqueness and efficient lookups (unique index)member_groups.member_id- For member → groups queriesmember_groups.group_id- For group → members queries- Composite index on
(member_id, group_id)- For uniqueness check
Query Optimization
Member Overview:
- Load groups with members in single query using query preloading
- Preload only necessary group attributes (id, name, slug) to minimize data transfer
- Filter groups at database level when filtering by group
N+1 Query Prevention:
- Always preload groups relationship when querying members
- Never access member groups without preloading (would trigger N+1 queries)
- Use query preloading mechanisms to load all required relationships in a single query
Performance Threshold:
- With proper preloading: Works efficiently up to 100 members (MVP scope)
- For larger datasets (>100 members), consider:
- Pagination (limit number of members loaded)
- Lazy loading of groups (only load when groups column is visible)
- Database-level aggregation for group counts
Group Detail:
- Paginate member list for large groups (>50 members)
- Load member count via calculation (not separate query)
- Use
Ash.Query.loadfor member details when displaying
Search Performance
Search Vector:
- Group names included in
search_vector(tsvector) - GIN index on
search_vectorfor fast full-text search - Trigger updates on
member_groupschanges
Filtering:
- Use database-level filtering (not in-memory)
- Leverage indexes for group filtering
Future Extensibility
Hierarchical Groups
Design for Future:
- Add
parent_group_idtogroupstable (nullable) - Add
parent_grouprelationship (self-referential) - Add validation to prevent circular references
- Add calculation for
path(e.g., "Parent > Child > Grandchild")
Migration Path:
- Add column with
NULLdefault (all groups initially root-level) - Add foreign key constraint
- Add validation logic
- Update UI to show hierarchy
Group Attributes
Future Attributes:
created_at/founded_date- Group creation datedissolved_at- Group dissolution datestatus- Active/inactive/suspendedcolor- UI color for badgesicon- Icon identifier
Migration Path:
- Add nullable columns
- Set defaults for existing groups
- Update UI to display new attributes
Roles/Positions in Groups
Future Feature:
- Add
member_group_rolestable - Link
MemberGrouptoRole(e.g., "Leiter", "Mitglied") - Extend
MemberGroupwithrole_idforeign key - Display role in member detail and group detail views
Group Permissions
Future Feature:
- Group-specific permission sets
- Role-based group access
- Member-level group management permissions
Feature Breakdown: Functional Units and MVP
Strategy: Vertical Slicing
The Groups feature is divided into functionally complete, vertical units. Each unit delivers a complete, usable functional area that can be independently tested and delivered.
MVP Definition
Minimal Viable Product (MVP): The MVP includes the core functionality necessary to manage groups and assign them to members:
- ✅ Create groups (Name + Description + Slug)
- ✅ Edit groups
- ✅ Delete groups (with confirmation)
- ✅ Assign members to groups
- ✅ Remove members from groups
- ✅ Display groups in member overview
- ✅ Filter by groups
- ✅ Sort by groups
- ✅ Display groups in member detail
- ✅ Groups in member search (automatically via search_vector)
Not in MVP:
- ❌ Hierarchical groups
- ❌ Roles/positions in groups
- ❌ Extended group attributes (dates, status, etc.)
- ❌ Group-specific permissions
Functional Units (Vertical Slices)
Unit 1: Group Management (Backend)
Functional Scope: Administrators can manage groups in the system
Scope:
- Group resource (Name, Description, Slug)
- CRUD operations for groups
- Validations (unique name, unique slug, length limits)
- Automatic slug generation from name
- Delete logic with cascade behavior
Deliverable: Groups can be created, edited, and deleted via API
Dependencies: None
Estimation: 4-5h
Unit 2: Member-Group Assignment (Backend)
Functional Scope: Members can be assigned to groups
Scope:
- MemberGroup join table
- Many-to-Many relationship
- Add/Remove member-group associations
- Cascade delete behavior
Deliverable: Members can be assigned to and removed from groups
Dependencies: Unit 1 (Groups must exist)
Estimation: 2-3h (can be combined with Unit 1)
Unit 3: Group Management UI
Functional Scope: Administrators can manage groups via web interface
Scope:
- Groups overview page (
/groups) - Group form (Create/Edit)
- Group detail page (member list)
- Delete confirmation modal (with name input)
Deliverable: Complete group management via UI possible
Dependencies: Unit 1 + 2 (Backend must be functional)
Estimation: 3-4h
Unit 4: Groups in Member Overview
Functional Scope: Groups are displayed in member overview and can be filtered/sorted
Scope:
- "Groups" column with badges
- Group filter dropdown
- Sorting by groups
- URL parameter persistence
Deliverable: Groups visible, filterable, and sortable in member overview
Dependencies: Unit 1 + 2 (Groups and assignments must exist)
Estimation: 2-3h
Unit 5: Groups in Member Detail
Functional Scope: Groups are displayed in member detail view
Scope:
- "Groups" section in Member Show
- Badge display
- Links to group detail pages
Deliverable: Groups visible in member detail
Dependencies: Unit 3 (Group detail page must exist)
Estimation: 1-2h
Unit 6: Groups in Member Search
Functional Scope: Group names are searchable in member search
Scope:
- Search Vector Update (Trigger)
- Fuzzy Search extension
- Test search functionality
Deliverable: Searching for group names finds associated members
Dependencies: Unit 1 + 2 (Groups and assignments must exist)
Estimation: 2h
Unit 7: Permissions
Functional Scope: Only administrators can manage groups
Scope:
- Add groups to permission sets
- Implement authorization policies
- UI permission checks
Deliverable: Permissions correctly implemented
Dependencies: All previous units (Feature must be functional)
Estimation: 1-2h
MVP Composition
MVP consists of:
- ✅ Unit 1: Group Management (Backend)
- ✅ Unit 2: Member-Group Assignment (Backend)
- ✅ Unit 3: Group Management UI
- ✅ Unit 4: Groups in Member Overview
- ✅ Unit 5: Groups in Member Detail
- ✅ Unit 6: Groups in Member Search (automatically via search_vector)
- ✅ Unit 7: Permissions
Total MVP Estimation: 13-15h
Implementation Order
Recommended Order:
-
Phase 1: Backend Foundation (Unit 1 + 2)
- Group resource
- MemberGroup join table
- CRUD operations
- Result: Groups can be managed via API
-
Phase 2: Management UI (Unit 3)
- Groups overview
- Group form
- Group detail
- Result: Groups can be managed via UI
-
Phase 3: Member Integration (Unit 4 + 5)
- Groups in overview
- Groups in detail
- Result: Groups visible in member views
-
Phase 4: Search Integration (Unit 6)
- Search Vector Update
- Result: Groups searchable
-
Phase 5: Permissions (Unit 7)
- Permission Sets
- Policies
- Result: Permissions correct
Issue Structure
Each functional unit can be implemented as a separate issue:
- Issue 1: Group Resource & Database Schema (Unit 1 + 2)
- Issue 2: Group Management UI (Unit 3)
- Issue 3: Groups in Member Overview (Unit 4)
- Issue 4: Groups in Member Detail (Unit 5)
- Issue 5: Groups in Member Search (Unit 6)
- Issue 6: Permissions (Unit 7)
Alternative: Issues 3 and 4 can be combined, as they both concern the display of groups.
Implementation Phases
Phase 1: MVP Core (Foundation)
Goal: Basic group management and member assignment
Tasks:
- Create
Groupresource (name, description, slug) - Implement slug generation (reuse
GenerateSlugchange from CustomFields) - Create
MemberGroupjoin table resource - Extend
Memberwith groups relationship - Database migrations (including slug column and unique index)
- Basic CRUD actions for groups
- Add/remove members from groups (via group management)
Deliverables:
- Groups can be created, edited, deleted
- Members can be added/removed from groups
- Basic validation and constraints
Estimation: 4-5h
Phase 2: UI - Groups Management
Goal: Complete groups management interface
Tasks:
- Groups index page (
/groups) - Group form (create/edit)
- Group show page (list members)
- Delete confirmation modal (with name input)
- Member count display
Deliverables:
- Full groups management UI
- Delete confirmation workflow
- Group detail view
Estimation: 3-4h
Phase 3: Member Overview Integration
Goal: Display and filter groups in member overview
Tasks:
- Add "Groups" column to member overview table
- Display group badges
- Group filter dropdown
- Group sorting
- URL query param persistence
- (Optional) Clickable group badges for filtering (enhanced UX)
Deliverables:
- Groups visible in member overview
- Filter by group (via dropdown)
- Sort by group
- (Optional) Clickable badges for quick filtering
Estimation: 2-3h
Phase 4: Member Detail Integration
Goal: Display groups in member detail view
Tasks:
- Add "Groups" section to member show page
- Display group badges
- Link to group detail pages
Deliverables:
- Groups visible in member detail
- Navigation to group pages
Estimation: 1-2h
Phase 5: Search Integration
Goal: Include groups in member search
Tasks:
- Update
search_vectortrigger to include group names - Extend
fuzzy_searchto search group names - Test search functionality
Deliverables:
- Group names searchable in member search
- Search finds members by group name
Estimation: 2h
Phase 6: Authorization
Goal: Implement permission-based access control
Tasks:
- Add groups to permission sets
- Implement authorization policies
- Test permission enforcement
- Update UI to respect permissions
Deliverables:
- Only admins can manage groups
- All users can view groups (if they can view members)
Estimation: 1-2h
Total Estimation: 13-18h
Note: This aligns with the issue estimation of 15h.
Issue Breakdown
Issue 1: Groups Resource & Database Schema
Type: Backend
Estimation: 4-5h
Tasks:
- Create
Groupresource (with slug attribute and generation) - Create
MemberGroupjoin table resource - Extend
Memberresource - Database migrations (including slug column)
- Basic validations (name, slug, description)
Acceptance Criteria:
- Groups can be created via Ash API
- Slug is automatically generated from name
- Slug is unique and immutable
- Members can be associated with groups
- Database constraints enforced (unique name, unique slug, foreign keys)
Issue 2: Groups Management UI
Type: Frontend
Estimation: 3-4h
Tasks:
- Groups index page
- Group form (create/edit)
- Group show page
- Delete confirmation modal
Acceptance Criteria:
- Groups can be created/edited/deleted via UI
- Delete requires name confirmation
- Member count displayed
Issue 3: Member Overview - Groups Integration
Type: Frontend
Estimation: 2-3h
Tasks:
- Add groups column with badges
- Group filter dropdown
- Group sorting
- URL persistence
- (Optional) Clickable group badges for filtering
Acceptance Criteria:
- Groups visible in member overview
- Can filter by group (via dropdown)
- Can sort by group
- Filter persists in URL
- (Optional) Can filter by clicking group badge
Issue 4: Member Detail - Groups Display
Type: Frontend
Estimation: 1-2h
Tasks:
- Add groups section to member show
- Display group badges
- Link to group pages
Acceptance Criteria:
- Groups visible in member detail
- Links to group pages work
Issue 5: Search Integration
Type: Backend
Estimation: 2h
Tasks:
- Update search vector trigger to include group names
- Extend fuzzy search to search group names
- Test search functionality
Acceptance Criteria:
- Group names searchable in member search (automatic via search_vector)
- Search finds members by group name
- Search vector updates automatically when member-group associations change
Note: This is part of MVP as group names are automatically included in full-text search once the search_vector trigger is updated.
Issue 6: Authorization
Type: Backend/Frontend
Estimation: 1-2h
Tasks:
- Add groups to permission sets
- Implement policies
- Test permissions
Acceptance Criteria:
- Only admins can manage groups
- All users can view groups (if they can view members)
Testing Strategy
Unit Tests
Group Resource Tests
File: test/membership/group_test.exs
Test Cases:
- Create group with valid attributes
- Return error when name is missing
- Return error when name exceeds 100 characters
- Return error when name is not unique
- Name uniqueness is case-insensitive
- Allow description to be nil
- Trim whitespace from name
- Description max length is 500 characters
- Slug is automatically generated from name on create
- Slug is immutable (doesn't change on update)
- Slug is unique (cannot have duplicates)
- Slug handles German umlauts correctly (ä → a, ß → ss)
- Slug converts to lowercase
- Slug removes special characters
- Slug replaces spaces with hyphens
- Slug truncates to max 100 characters
- Slug cannot be empty (validation fails if slug would be empty)
- Read group by slug (via identity lookup)
- Update group name and description
- Prevent duplicate name on update
- Prevent duplicate slug on create (same name → same slug → error)
- Delete group and all member associations
- Do not delete members themselves
- Member count calculation returns 0 for empty group
- Member count calculation returns correct count when members added
- Member count updates correctly when members removed
MemberGroup Resource Tests
File: test/membership/member_group_test.exs
Test Cases:
- Create association between member and group
- Prevent duplicate associations
- Cascade delete when member deleted
- Cascade delete when group deleted
Integration Tests
Member-Group Relationships
File: test/membership/group_integration_test.exs
Test Cases:
- Member can belong to multiple groups
- Group can contain multiple members
UI Tests
Groups Management
File: test/mv_web/live/group_live/index_test.exs
Test Cases:
- List all groups
- Create new group
- Delete group with confirmation
Member Overview Integration
File: test/mv_web/live/member_live/index_groups_test.exs
Test Cases:
- Display group badges
- Filter members by group
Migration Strategy
Database Migrations
Migration 1: Create groups table
- Create table with UUID v7 primary key
- Add name field (required, unique, case-insensitive)
- Add slug field (required, unique, case-sensitive, auto-generated)
- Add description field (optional)
- Add timestamps
- Create unique index on lowercased name (for name uniqueness)
- Create unique index on slug (for slug uniqueness and lookups)
- Create index on lowercased name for search
Note: Slug generation follows the same pattern as CustomFields:
- Uses
Mv.Membership.CustomField.Changes.GenerateSlug(reusable change) - Or create
Mv.Membership.Group.Changes.GenerateSlugif needed - Slug is generated on create, immutable on update
Migration 2: Create member_groups join table
- Create table with UUID v7 primary key
- Add member_id and group_id foreign keys
- Add timestamps
- Create unique index on (member_id, group_id)
- Create indexes on member_id and group_id
- Add foreign key constraints with CASCADE delete
Migration 3: Update search_vector trigger (if needed)
- Extend trigger to include group names
- Update trigger function
Code Migration
Ash Resources:
- Use code generation tools to generate migrations
- Manually adjust if needed
Domain Updates:
- Add groups resources to domain
- Define domain actions
Summary
This architecture provides a solid foundation for the Groups feature while maintaining flexibility for future enhancements. The many-to-many relationship is implemented via a join table, following existing patterns in the codebase. The MVP focuses on core functionality (create, edit, delete groups, assign members) with clear extension points for hierarchical groups, roles, and advanced permissions.
Slug Implementation: Groups include automatic slug generation, following the same pattern as CustomFields. Slugs are:
- Automatically generated from the
nameattribute on create - Immutable after creation (don't change when name is updated)
- Unique and URL-friendly
- Available for future route enhancements (e.g.,
/groups/:sluginstead of/groups/:id)
The implementation reuses the existing GenerateSlug change from CustomFields, ensuring consistency across the codebase.
The implementation is split into 6 manageable issues, totaling approximately 15 hours of work, aligning with the original estimation. Each phase builds on the previous one, allowing for incremental development and testing.