Skip to content

Account Boundary Context

Arine edited this page Nov 16, 2023 · 5 revisions

Naive Requirements

  • A user can sign in one of the roles: member or admin
  • Identity aggregate issues and validate JWT token that also maintains roles
  • Basic functionalities - sign up, sign in and sign out are required, and sign in is established by emails and password
  • When entering my page, the user's information should be shown as follows
    • Name, email, preferred language

Ubiquitous Language and Specific Policy

User is everyone who uses this service regardless of the purpose. User has roles - Member and Administrator. Member means someone who signs up to use the system from the outside, and Administrator means someone who operates and develops the system. A User can have one or two roles, but only one can be chosen when signing in with an e-mail and password.

Identity is something that can prove who the requestor is. We'll use JWT, and the payload should have the role that a user chooses when signing in.

When requesting Sign Up, the user must provide this information: e-mail, password, name, preferred language. E-mail is a key for Sign in, so it should be unique. The role is pre-defined as Member with the public Sign Up API. The password must be encrypted securely.

The user must provide e-mail and password information when requesting Sign In. When the password matches, our system responds with two JWT tokens: access token (with the role in the claim) and refresh token. The refresh token is persisted to detect refresh token reuse.

The user's refresh token is removed when requesting Sign Out.

Boundary Context and Aggregate

If I were to design in the real world, I would use DDD Crew's Bounded Context Canvas to dive deep. This is an examination for me so I will skip it.

Based on the current requirements, Identity and User aggregates cooperate closely, and the incoming traffic variance would be similar. So, I determined that two aggregates in a single bounded context would be beneficial, and I named the bounded context Account. Account will be the first service, which means two aggregates are deployed in a single service.

API Design

  • POST /<bounded context>/<owner aggregate>/<command>
  • GET /<bounded context>/<owner aggregate>/<query>

A bounded context will be a single deployment. In this situation, I agonised over whether an aggregate name should be in the API path. Just for now, it could add complexity and extra naming burden. However, what if we split the bounded context into two bounded contexts - User and Identity bounded contexts? In this situation, we should convert internal calls to external API calls and would be in trouble deciding which API belongs to which aggregate. I assumed it is good to determine the owner aggregate for an API in advance.

Business Flow Consideration

When a single request from a client is required to execute multiple commands over aggregates? For instance, assume that a user is signing in. User aggregate executes the SignInWithCredential command to verify the given password is correct, and Identity aggregate executes IssueAccessToken to transfer the JWT token to the client. There are several ways to achieve the flow.

---
title: Option 1 - Call Identity Aggregate from Account Interface (Application Service)
---

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: Sign in
    Interface ->>+ User: VerifyCredential
    User ->> User: Verify ID and password
    User ->>- Interface: OK
    Interface ->>+ Identity: IssueAccessToken
    Identity ->>- Interface: OK
    Interface ->>- Client: OK
---
title: Option 2 - Call Identity Aggregate from User Aggregate
---

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: Sign in
    Interface ->>+ User: VerifyCredential
    User ->> User: Verify ID and password
    User ->>+ Identity: IssueAccessToken
    Identity ->>- User: OK
    User ->>- Interface: OK
    Interface ->>- Client: OK

Option 1 decouples business flow concerns into the application service layer. This choice would be great when introducing BFF and context manager (or SAGA) because we only need to move the business flow into the orchestration mechanism.

Option 2 recognises that issuing an access token is a part of the user's sign-in business logic. So, it assumes that the User aggregate must know the existence of the Identity aggregate (or service) and interact with it directly.

There are pros and cons, and they may differ in every case. For this exercise, I will go to Option 1. I prefer to separate business flow from the single domain.

Sequence Diagram

All diagrams illustrate the happy path of use-cases.

Sign Up

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: POST /account/user/sign-up
    Interface ->>+ User: RegisterUser
    User ->> User: Execute
    User ->>- Interface: OK
    Interface ->>+ Identity: RegisterIdentity
    Identity ->> Identity: Execute
    Identity ->>- Interface: OK
    Interface ->>- Client: OK

Sign In

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: POST /account/user/sign-in
    Interface ->>+ User: VerifyCredential
    User ->>- Interface: OK
    Interface ->>+ Identity: IssueTokens
    Identity ->>- Interface: OK
    Interface ->>- Client: OK

Sign Out

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: POST /account/user/sign-out
    Interface ->>+ Identity: InvalidateTokens
    Identity ->>- Interface: OK
    Interface ->>- Client: OK

Refresh Access Token

sequenceDiagram
    actor Client
    participant Interface as Account Interface
    participant User as User Aggregate
    participant Identity as Identity Aggregate

    Client ->>+ Interface: POST /account/identity/refresh-tokens
    Interface ->>+ Identity: RefreshTokens
    Identity ->>- Interface: OK
    Interface ->>- Client: OK