ums

Approvals BC — Consistency Rules

Bounded Context: Ums.Domain.Approvals Aggregates: ApprovalWorkflow, ApprovalRequest, DocumentType, NotificationRule, UserDocument, AccessEnforcementPolicy


ApprovalWorkflow

State Machine

No explicit status enum currently. Consistency enforced via RequiresApproval flag and required documents collection.

Planned: WorkflowStatus: { Draft, Active, Archived } — not yet implemented.

Intra-aggregate Guards

Operation Guard Broken Rule  
AddRequiredDocument() DocumentType must not already be required approvals.document_type_already_required  
Create() If RequiresApproval = true, at least one RequiredDocument must be added approvals.requires_documents_if_approval_required  
RemoveRequiredDocument() If RequiresApproval = true, must leave ≥ 1 document approvals.requires_documents_if_approval_required ### Orphan Risks
Risk Scenario Mitigation  
Workflow with 0 required docs but RequiresApproval=true RemoveRequiredDocument() empties the list Guard: validate min count before remove

ApprovalRequest

State Machine

Pending ──Approve()──► Approved (terminal)
Pending ──Reject()──► Rejected (terminal)

Planned extensions: Revoked, Expired states — not yet implemented.

Intra-aggregate Guards

Operation Guard Broken Rule  
Approve() / Reject() Status must be Pending approvals.request_not_pending ### Cross-Aggregate Dependency Guards (application layer validates before calling)
Operation Validation Notes  
Create() WorkflowId must exist and be active Application layer pre-validates  
Create() RequestedRoleId must exist and be active Application layer pre-validates  
Approve() GrantedRoleId must exist and be active Application layer pre-validates ### Orphan Risks
Risk Scenario Mitigation  
Pending request references deleted Role Role deleted after request created Application layer listens to RoleDeactivatedEvent, cancels pending requests  
Pending request never resolved No timeout mechanism Planned: background job to expire after N days

DocumentType

State Machine

DocumentType does not have an explicit status state machine. It is managed via Criticity level and IsActive flag.

Intra-aggregate Guards

Operation Guard Broken Rule  
Create() Code must be unique within tenant common.duplicate  
Criticity rules Critical document type must have at least one active AccessEnforcementPolicy Validated at query/reporting level ### Design Decision: NotificationRule

Code is the source of truth. NotificationRule is implemented as anindependent Aggregate Rootin Ums.Domain.Approvals.NotificationRule. It isnotan owned entity of DocumentType. DocumentType may reference NotificationRule entities by ID, but does not own their lifecycle. The document-type.md documentation has been updated to reflect this.


NotificationRule

Aggregate Root: Ums.Domain.Approvals.NotificationRule

NotificationRule is anindependent Aggregate Root — not an owned entity of DocumentType.

State Machine

Active ──Deactivate()──► Inactive

Intra-aggregate Guards

Operation Guard Broken Rule  
Deactivate() Must not already be inactive approvals.rule_already_inactive ### Orphan Risks
Risk Scenario Mitigation  
Deactivated rule referenced by DocumentType DocumentType references inactive rule Application layer should filter inactive rules from notifications

UserDocument

State Machine

PendingReview ──Validate()──► Valid ──Expire()──► Expired
PendingReview ──Reject()──► Rejected
Rejected ──ReUpload()──► PendingReview
Expired ──ReUpload()──► PendingReview

Planned: Archived state after Expired with retention period.

Intra-aggregate Guards

Operation Guard Broken Rule  
Validate() / Reject() Status must be PendingReview compliance.document_not_pending_review  
Expire() Status must not already be Expired compliance.document_already_expired  
Any invalid transition E.g. Valid → PendingReview compliance.document_cannot_transition  
Upload() ExpirationDate must be after IssueDate compliance.expiration_before_issue_date ### Child Entity Cascade Rules
Event Cascade  
Any state change AccessNotification entries are append-only audit records. No cascade required.

AccessEnforcementPolicy

State Machine

Active ──Deactivate()──► Inactive

Intra-aggregate Guards

Operation Guard Broken Rule  
Create() Must target at least one Profile or Role approvals.policy_requires_profile_or_role  
Deactivate() Must not already be inactive approvals.policy_already_inactive

Part of consistency-rules/index.md