/

Developer Guide

Identity & Access Context

Subdomain type: Supporting · Grounded in: User Stories IA-1 through IA-6

This page captures the design-time model of the Identity & Access context: the language, the aggregates, and the rules we committed to before writing code. It deliberately stays at the conceptual level — concrete routes, storage, and wire formats are deliberately left open, decisions for whoever builds this model.

The context answers three questions: who is this principal, which organizations do they belong to, and what may they do there. It is the upstream source of identity for the platform — it produces the identity token that every other context treats as a Published Language.


Ubiquitous Language

TermMeaning in this context
AccountA principal (human or system) that can authenticate.
DomainAn organization or tenant. The unit of multi-tenancy.
MembershipThe link that places an Account inside a Domain with a specific Role.
RoleA ranked permission level used for authorization decisions.
Auth StrategyHow a Domain verifies identity: managed internally, or delegated to an external identity provider.

Aggregates

We model two aggregate roots — Account and Domain. They are kept separate on purpose: an Account refers to a Domain only by its name (the shared identifier described in Strategic Design, so the two can evolve independently rather than through a hard reference.

classDiagram
    class Account {
        <<Aggregate Root>>
        +name
        +email
        +credential
        +secondFactorSeed
        +Membership[] memberships
    }
    class Membership {
        <<Value Object>>
        +domainName
        +Role role
        +externalId
    }
    class Domain {
        <<Aggregate Root>>
        +name
        +visibleFromOutside
        +authStrategy
        +Domain[] subdomains
        +InviteSecrets inviteSecrets
        +SSOConfig ssoConfig
    }
    class SSOConfig {
        <<Value Object>>
        +issuerUrl
        +clientId
        +clientSecret
    }
    class InviteSecrets {
        <<Value Object>>
        +perRoleSecret
    }
    class Role {
        <<Value Object>>
        +name
        +rank
    }

    Account "1" *-- "many" Membership : holds
    Membership "1" --> "1" Role : carries
    Domain "1" *-- "0..1" SSOConfig : may hold
    Domain "1" *-- "1" InviteSecrets : holds

Aggregate: Account

The consistency boundary around a principal and its memberships.

This shape directly answers story IA-1 (one account recognized across organizations) and IA-5 (one sign-in, every membership carried together).

Aggregate: Domain

The consistency boundary around an organization and the rules under which principals join it.

authStrategy and SSOConfig together answer story IA-3 (sign in through the company’s own identity provider); subdomains answers IA-4 (campus/department structure); visibleFromOutside answers IA-6 (a domain invisible to public discovery).

Value Objects: Membership, SSO config, invite secrets

The smaller concepts living inside these aggregates are modelled as immutable, identity-less value objects:


The Role policy

Role is modelled as a ranked value object rather than a free-form label, so that authorization can be expressed as “at least this powerful” rather than an enumeration of cases. The comparison is the single rule the context exposes for permission checks:

role_satisfies(held, required):
    return rank(held) >= rank(required)

ranking (high to low):
    admin > business_admin > business_staff > standard_customer

Where in the request path this comparison is invoked — and against which required rank — is left open at the model level; the rule only states that it must hold. A ranked Role, rather than an enumerated list of cases, is what lets story IA-2 grant a member a role more powerful than an ordinary one from their first sign-in.


Domain Services

Two pieces of behaviour do not belong to a single entity, so we foresee them as domain services:

ServiceResponsibility
Identity token issuanceAssemble and sign the identity token, embedding the principal’s memberships so downstream contexts can authorize on their own.
Role invitationDerive and validate the short, time-bounded codes that grant a Role within a Domain.

Published Language: the identity token

The output of this context is a signed identity token whose contents — the principal, their memberships, roles, and domains — are the contract every other context conforms to. Designing this payload as a stable Published Language is what lets the other contexts authorize locally without calling back here.

sequenceDiagram
    participant U as Client
    participant A as Identity & Access
    U->>A: authenticate (credentials)
    Note over A: resolve the account and its memberships
    Note over A: assemble identity token (memberships, roles, domains)
    A-->>U: signed identity token

The interaction above is intentionally generic: the exact endpoints, token format, and federation handshake are design decisions deferred to the implementation. What matters at the domain level is that authentication yields a self-contained statement of identity and authority.