// Domain-driven user aggregatetypeUserinterface{ID()uintFirstName()stringLastName()stringEmail()stringHasPassword()boolIsActive()boolRoles()[]RolePermissions()[]PermissionSetEmail(emailstring)UserSetFirstName(namestring)UserSetPassword(passwordstring)User// ... more setters (immutable pattern)}// Private struct implementationtypeuserstruct{iduintemailstringpasswordstringroles[]Role// ...}// Functional options for constructionfuncNew(opts...Option)User{u:=&user{...}for_,opt:=rangeopts{opt(u)}returnu}funcWithEmail(emailstring)Option{returnfunc(u*user){u.email=email}}
Service Business Logic
// UserService with dependency injectiontypeUserServicestruct{repouser.Repositoryvalidatoruser.Validatorpublishereventbus.EventBus}// Permission checkingfunc(s*UserService)GetByID(ctxcontext.Context,iduint)(user.User,error){// Check permissioniferr:=composables.CanUser(ctx,permissions.UserRead);err!=nil{returnnil,err}// Get from repositoryreturns.repo.GetByID(ctx,id)}// Transactional operationsfunc(s*UserService)Create(ctxcontext.Context,datauser.User)(user.User,error){iferr:=composables.CanUser(ctx,permissions.UserCreate);err!=nil{returnnil,err}varcreateduser.Usererr:=composables.InTx(ctx,func(txCtxcontext.Context)error{iferr:=s.validator.ValidateCreate(txCtx,data);err!=nil{returnerr}varerrerrorcreated,err=s.repo.Create(txCtx,data)returnerr})iferr==nil{s.publisher.Publish(user.NewCreatedEvent(ctx,created))}returncreated,err}
Repository Implementation
// User repository implementationfunc(r*userRepository)GetByID(ctxcontext.Context,iduint)(user.User,error){constop="user.Repository.GetByID"tenantID,err:=composables.UseTenantID(ctx)iferr!=nil{returnnil,serrors.E(op,err)}row:=composables.UseTx(ctx,r.db).QueryRow(`SELECT id, email, first_name, last_name, password
FROM users
WHERE id = $1 AND tenant_id = $2`,id,tenantID,)varuuser.Useriferr:=scanUser(row,&u);err!=nil{iferrors.Is(err,sql.ErrNoRows){returnnil,serrors.E(op,serrors.KindNotFound)}returnnil,serrors.E(op,err)}returnu,nil}
Permission Checking Pattern
// Composables for permission management// File: pkg/composables/permission.go// Check if user has permissionfuncCanUser(ctxcontext.Context,permissionstring)error{user:=ctx.Value("user").(User)perms:=ctx.Value("permissions").([]string)for_,p:=rangeperms{ifp==permission{returnnil}}returnErrPermissionDenied}
Permission Matrix
User Permissions
Permission
Resource
Action
Modifier
Description
users:create:all
Users
Create
All
Create any user
users:read:all
Users
Read
All
View all users
users:read:own
Users
Read
Own
View own profile
users:update:all
Users
Update
All
Update any user
users:update:own
Users
Update
Own
Update own profile
users:delete:all
Users
Delete
All
Delete any user
Role Permissions
Permission
Resource
Action
Modifier
Description
roles:create:all
Roles
Create
All
Create roles
roles:read:all
Roles
Read
All
View all roles
roles:update:all
Roles
Update
All
Update roles
roles:delete:all
Roles
Delete
All
Delete roles
Group Permissions
Permission
Resource
Action
Modifier
Description
groups:create:all
Groups
Create
All
Create groups
groups:read:all
Groups
Read
All
View groups
groups:update:all
Groups
Update
All
Update groups
groups:delete:all
Groups
Delete
All
Delete groups
API Contracts
User Endpoints
GET /users # List users (paginated)
GET /users/:id # Get user details
POST /users # Create user
PUT /users/:id # Update user
DELETE /users/:id # Delete user
POST /users/:id/roles # Assign roles
DELETE /users/:id/roles/:roleId # Unassign role
Role Endpoints
GET /roles # List roles
GET /roles/:id # Get role details
POST /roles # Create role
PUT /roles/:id # Update role
DELETE /roles/:id # Delete role
GET /roles/:id/permissions # Get role permissions
POST /roles/:id/permissions # Add permission to role
Group Endpoints
GET /groups # List groups
GET /groups/:id # Get group details
POST /groups # Create group
PUT /groups/:id # Update group
DELETE /groups/:id # Delete group
POST /groups/:id/users # Add user to group
DELETE /groups/:id/users/:uid # Remove user from group
Authentication Endpoints
POST /login # Authenticate user
POST /logout # Invalidate session
GET /account # Get user account
PUT /account # Update account
Error Handling
All services use serrors package for error handling:
constopserrors.Op="UserService.GetByID"// Wrap errors with contextiferr!=nil{returnnil,serrors.E(op,err)}// Use error kindsifnotFound{returnnil,serrors.E(op,serrors.KindNotFound)}// Provide contextifinvalid{returnnil,serrors.E(op,serrors.KindValidation,"email is required")}
Multi-tenancy Implementation
Tenant Isolation
Database Level
All tables include tenant_id foreign key
WHERE clauses always filter by tenant_id
Context Level
// Get tenant from contexttenantID:=composables.UseTenantID(ctx)// All queries filtered by tenantdb.QueryRow("SELECT * FROM users WHERE id = $1 AND tenant_id = $2",userID,tenantID,)
Service Level
Permission checks include tenant context
User queries scoped to tenant
Session tokens tied to tenant
Performance Considerations
Database Indexes
-- User queriesCREATEINDEXusers_tenant_id_idxONusers(tenant_id);CREATEINDEXusers_first_name_idxONusers(first_name);CREATEINDEXusers_last_name_idxONusers(last_name);-- Session lookupsCREATEINDEXsessions_tenant_id_idxONsessions(tenant_id);CREATEINDEXsessions_user_id_idxONsessions(user_id);CREATEINDEXsessions_expires_at_idxONsessions(expires_at);-- Permission lookupsCREATEINDEXrole_permissions_role_id_idxONrole_permissions(role_id);CREATEINDEXrole_permissions_permission_id_idxONrole_permissions(permission_id);
Query Optimization
Batch loading: Load user roles/permissions with single query
Caching: Permission checks cached for session duration
Pagination: Large datasets paginated with limit/offset
Connection pooling: Database connection reuse via composables.UseTx()