A request, end to end

A write: creating a workspace

CreateTeamWhenWorkspaceCreated…IUnitOfWorkIWorkspacesRepositoryWorkspaceCreationValidatorIWorkspacesServiceWorkspacesControllerBrowserCreateTeamWhenWorkspaceCreated…IUnitOfWorkIWorkspacesRepositoryWorkspaceCreationValidatorIWorkspacesServiceWorkspacesControllerBrowserPOST /api/v1/workspacesTryAddAsync(workspaceCreation)validate: name, colour, plan limitValidationResultAddAsync(workspace)workspace.MarkAsCreated() raises a domain eventValidationResult<Workspace>CommitAsync()the interceptor dispatches the domain eventcreate the workspace's teamone transaction: workspace, team, outbox row201 Created
CreateTeamWhenWorkspaceCreated…IUnitOfWorkIWorkspacesRepositoryWorkspaceCreationValidatorIWorkspacesServiceWorkspacesControllerBrowserCreateTeamWhenWorkspaceCreated…IUnitOfWorkIWorkspacesRepositoryWorkspaceCreationValidatorIWorkspacesServiceWorkspacesControllerBrowserPOST /api/v1/workspacesTryAddAsync(workspaceCreation)validate: name, colour, plan limitValidationResultAddAsync(workspace)workspace.MarkAsCreated() raises a domain eventValidationResult<Workspace>CommitAsync()the interceptor dispatches the domain eventcreate the workspace's teamone transaction: workspace, team, outbox row201 Created
  1. The controller maps the request body to an input record and calls one driving port. It holds no rule.
  2. The service validates — the plan limit comes from Organizations' public contract — adds the entity through its repository and lets the entity raise WorkspaceCreatedDomainEvent.
  3. The controller commits. Before SaveChanges, an interceptor dispatches the domain event: the team is created in the same transaction, and the integration event for the other domains joins the outbox.
  4. After the commit, the outbox sends WorkspaceCreatedIntegrationEvent, and the Webhooks module dispatches it to the endpoints the organization subscribed. (Creating a workspace is not audited — it is reversible; deleting one is.)

A read: listing an organization's members

IOrganizationMembersApiQueryRepositoryIOrganizationMembersApiQueryServiceMembersControllerBrowserIOrganizationMembersApiQueryRepositoryIOrganizationMembersApiQueryServiceMembersControllerBrowserGET /api/v1/organizations/{id}/members?page=…GetAsync(filter)projection query, straight to SQLOffsetPage<DetailedOrganizationMember>200 OK
IOrganizationMembersApiQueryRepositoryIOrganizationMembersApiQueryServiceMembersControllerBrowserIOrganizationMembersApiQueryRepositoryIOrganizationMembersApiQueryServiceMembersControllerBrowserGET /api/v1/organizations/{id}/members?page=…GetAsync(filter)projection query, straight to SQLOffsetPage<DetailedOrganizationMember>200 OK

No entity is materialised and no domain service runs. The member's name and avatar are joined from users_view in the same query.