Agency Manager Domain Model
Purpose
This is the planning-level domain model for the product.
It is not a final schema. It is the model that pages, capabilities, and backend boundaries should align to.
Core Entities
Agency
Represents the operating business using the product.
Owns:
- agency identity and profile
- billing configuration for the agency's own subscription to the SaaS later
- staff membership context
- default operating rules
Surface:
org- later
platform
Agency User
Represents a staff user of the agency.
Owns:
- org membership
- base role
- client access scope
- optional user-specific capability overrides
- profile and notification preferences
Surface:
org
Client Access Assignment
Represents the agency-side visibility relationship between an agency user and a client account.
Owns:
- agency user reference
- client account reference
- assignment state
- who granted access
- timestamps
Surface:
org
Truth note:
The first model should stay simple:
- the agency owner defaults to full business oversight across all client accounts
- some agency users may have access to all clients
- others may be assigned to specific clients only
Recommended persistence note:
- base role should live with membership state
- selected-client access should be modeled separately from role policy
Agency Role Policy
Represents the agency-owned permission policy for non-owner staff roles.
Owns:
- default role templates used by the agency
- capability toggles per role
- delegation rules for staff invites, portal invites, billing visibility, and similar operational actions
- update history
Surface:
org
Truth note:
The first practical model should be template-first, not blank-slate enterprise RBAC:
- start from common roles
- let the owner adjust what those roles can do
- allow user-specific overrides only when needed
Recommended persistence note:
- one organization-level policy record is the right first shape
- owner is not just another editable role inside that policy
Agency Permission Override
Represents a sparse exception applied to one agency user's default role policy.
Owns:
- agency user reference
- explicitly allowed capabilities
- explicitly denied capabilities
- who changed the override
- timestamps
Surface:
org
Truth note:
- overrides should be exceptional, not the main permission model
- if many users need overrides, the base role policy is wrong
Client Account
Represents a customer relationship managed by one agency.
Owns:
- company or client identity
- service relationship metadata
- account status
- primary contacts
- billing identity and defaults used to provision/sync the Stripe customer
- internal billing mapping link
- project portfolio link
Surface:
org- summarized into
portal
Portal User
Represents an org-scoped portal identity for one external person inside one agency tenant.
Owns:
- login/access to the client portal realm for that agency organization
- the durable external identity bound to the auth account
Does not directly own:
- company-specific role
- company-specific permission scope
Those belong to company access grants.
Portal Access Grant
Represents one company-specific access grant linking a portal identity to one client account and one client-local person record.
Owns:
- company-specific role
- company-specific status
- company-specific permission scope
- the link between
portalUser,clientAccount, andclientContact
Surface:
portal
Engagement
Represents the commercial relationship or service package between agency and client.
Examples:
- monthly retainer
- website redesign engagement
- SEO engagement
- support plan
Owns:
- commercial model
- service start and end dates
- budget or retainer context
- linked projects
- optional context for subscriptions or recurring service relationships
Surface:
org- summarized into
portal
Project
Represents a tracked delivery unit under a client account or engagement.
Owns:
- title and scope
- status
- owners
- dates
- milestone and deliverable relationships
- client-visible summary
Surface:
org- summarized into
portal
Project Update
Represents a client-visible progress update, not internal scratch capture.
Owns:
- message or summary
- visibility flag
- timestamps
- optional links to deliverables
Surface:
- authored in
org - consumed in
portal
Note
Represents internal working memory that may be personal or linked to a client or project.
Owns:
- internal capture
- author-private visibility in the first slice
- optional client-account link
- optional project link
- pin and archive state
Surface:
- authored in
org - not projected into
portal
Time Entry
Represents work logged by agency staff.
Owns:
- staff owner
- client account reference
- optional project reference
- duration
- date
- billable state
- notes
Surface:
org
Billing Profile
Represents the internal app-owned Stripe mapping for a client account.
Owns:
- Stripe customer linkage
- sync/health context around that linkage
- account-level billing anchor for invoices and subscriptions
Surface:
- internal backend layer
- summarized into
organdportalthrough the client account and billing surfaces
Truth note:
This is not the finance ledger, and it is not meant to be a primary user-facing route. It is the internal app context around Stripe truth.
Invoice Record
Represents mirrored invoice state needed for app workflows.
Owns:
- invoice identifiers
- amount summary
- status summary
- due date
- payment outcome summary
- optional engagement or project context when relevant, without requiring it
Surface:
orgportal
Truth note:
Invoice records should mirror Stripe state instead of competing with it.
Subscription Record
Represents mirrored recurring billing state needed for app workflows.
Owns:
- subscription identifiers
- status summary
- billing period summary
- service label or retainer label
- optional engagement or project context when relevant, without requiring it
Surface:
orgportal
Truth note:
Subscription records should be anchored to the client account first. Project or engagement linkage is optional context, not a required billing dependency.
Document
Represents a shared file or managed artifact.
Owns:
- storage reference
- display metadata
- client account or project relationship
- visibility scope
- uploader
- source side (agency or client)
- timestamps
Surface:
- authored in
org - consumed in
portal
File Request
Represents a lightweight request from the agency asking the client for a specific file or material.
Owns:
- client account reference
- optional project reference
- request title or instruction
- requested by
- requested from
- status
- due date when relevant
- submitted file linkage when fulfilled
Surface:
- authored in
org - fulfilled and viewed in
portal
Truth note:
File requests should structure collaboration, not replace the shared drive. Normal uploads should still be possible without creating a formal request every time.
Notification
Represents app-visible or email-visible notices relevant to users.
Owns:
- audience
- kind
- severity
- call to action
- delivery state
Surface:
orgportal- later
platform
Activity Event
Represents a timeline event for significant account, project, billing, or document actions.
Owns:
- actor
- timestamp
- event kind
- related entity reference
- summary
Surface:
- primarily
org - selectively exposed to
portal
Relationship Direction
- one
Agencyhas manyAgency Users - one
Agencyhas manyClient Accounts - one
Client Accounthas manyClient Users - one
Client Accounthas one or moreEngagements - one
Client Accounthas one or moreProjects - one
Projectcan belong to oneEngagement - one
Projecthas manyProject Updates - one
Agency Usercan create manyTime Entries - one
Client Accounthas one internalBilling Profile - one
Client Accounthas manyInvoice Records - one
Client Accounthas manySubscription Records - one
Client Accounthas manyDocuments - one
Client Accounthas manyFile Requests - one
Projectcan have manyDocuments - one
Projectcan have manyFile Requests - many entities can emit
NotificationsandActivity Events
Boundary Rules
Keep client identity separate from agency identity
Portal users are not just weaker org users.
Keep billing truth separate from project truth
Do not drive invoice state from project status fields.
Keep high-churn records separate from account profiles
Time entries, notifications, and activity events should stay in their own records rather than bloating client or project documents.
Keep client-visible summaries separate from internal-only notes
The same underlying relationship can have both internal and client-visible views, but those should not be the same field or the same document responsibility.
Later Optional Entities
These can exist later if the product proves the need:
ApprovalTaskSupportThreadProposalAutomationRuleTemplate
Do not start with them unless a real workflow forces the need.