Phase 2 uses a layered modular architecture with explicit dependency direction.
Domain
^
|
Application ----> Contracts
^
|
Infrastructure
^
|
Presentation
The important compile-time direction is that Infrastructure implements Application abstractions, while Presentation acts as the composition root.
Owns core domain entities and domain-level concepts.
Dependencies:
Domain -> none
Examples:
EventSeatBookingUserRefreshToken
Owns application behavior and use cases.
Dependencies:
Application -> Domain
Application -> Contracts
Examples:
BookingsServiceEventsServiceSeatsServiceAuthenticationService- Repository interfaces
- Identity/token abstractions
- Application result models
Application code does not reference EF Core, AppDbContext, UserManager<User>, SQL Server exceptions, or JWT implementation types.
Contains transport contracts used by the API:
- Request DTOs
- Response DTOs such as
TokenResponse
Contracts remain independent of Domain and Infrastructure concerns.
Implements Application abstractions and owns technical infrastructure.
Dependencies:
Infrastructure -> Application
Infrastructure -> Domain
Examples:
AppDbContext- EF Core configurations
- Repositories
- EF Core migrations
IdentityServiceTokenService
IdentityService adapts ASP.NET Core Identity to IIdentityService.
TokenService handles JWT creation and refresh-token persistence behind ITokenService.
The persistence flow is:
Application Service
|
v
Repository Interface
|
v
Repository Implementation
|
v
AppDbContext
|
v
SQL Server
AppDbContext and migrations are located in Infrastructure.
Authentication follows:
AuthenticationController
|
v
IAuthenticationService
|
v
AuthenticationService
/ v v
IIdentityService ITokenService
| |
v v
IdentityService TokenService
| |
v +--> JWT
UserManager |
+--> RefreshToken/AppDbContext
The Application service owns the decisions involved in registration, login, activation, and deactivation.
Infrastructure owns the framework-specific implementation.
Presentation owns:
- Controllers
- HTTP status codes
- ProblemDetails/ValidationProblemDetails translation
- API versioning
- Swagger/OpenAPI
- Authentication configuration
- Middleware
- Composition root
A controller should generally follow:
HTTP request
|
v
Application service
|
v
Application result
|
v
HTTP response
Presentation composes the application:
builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);Infrastructure provides the concrete implementations required by Application.
Infrastructure exposes a migration operation, while Presentation decides when to run it.
Conceptually:
Presentation startup
|
| Development?
v
Infrastructure migration method
|
v
AppDbContext.Database.MigrateAsync()
This keeps migration ownership inside Infrastructure without making Presentation responsible for EF Core persistence details.
Controller Unit Tests
|
v
Application Unit Tests
|
v
Infrastructure Tests
|
v
Integration Tests
|
v
SQL Server
Each layer tests its own responsibility:
- Controllers test HTTP/result mapping.
- Application tests business/use-case behavior with mocked abstractions.
- Infrastructure tests adapter and persistence behavior.
- Integration tests verify the application working against SQL Server.
Phase 2 deliberately stops short of introducing distributed infrastructure.
Not yet included:
- Full refresh-token rotation/renewal workflow
- Message broker/event bus
- Outbox pattern
- Distributed caching
- Background workers
- Cloud deployment
- Distributed tracing
Those capabilities can be introduced incrementally on top of this modular foundation.