Skip to content

Architecture Design

project-spec -> architecture-design -> domain-develop -> application-develop -> adapter-develop -> observability-develop -> test-develop

If 00-project-spec.md generated by the project-spec skill exists, it is automatically read to confirm Aggregate candidates and use case overview. If not, the user is asked directly about the project scope.

Once Aggregate candidates and business rules are defined, the next question is “Where and under what names do we place this code?” Project structure, layer separation, naming conventions, and infrastructure decisions must be agreed upon before writing code. Changing them later requires renaming paths and files everywhere.

The architecture-design skill designs the project skeleton following the Functorium framework’s layered architecture principles. It organizes solution configuration, folder tree, naming conventions, project reference direction, and persistence/observability/API infrastructure decisions into a single document.

PhaseActivityDeliverable
1. Project Structure DecisionProject name, solution separation level, Adapter separation approachSolution configuration draft
2. Layer CompositionDomain/Application/Adapter/Host folder treePer-layer folder structure
3. Infrastructure DecisionsPersistence, observability, HTTP API, external service strategyInfrastructure decision specification
4. Document GenerationOrganize all content into a structured document01-architecture-design.md
Design the architecture
Set up the project structure
Design the layers
Configure the solution
Determine the folder structure

The skill determines the following through conversation.

Basic Decisions:

  • Project naming (e.g., AiGovernance, ECommerce)
  • Single solution vs multi-solution
  • Adapter project separation level:
    • Single: {Name}.Adapters — Suitable for small projects
    • Separated: {Name}.Adapters.Infrastructure + {Name}.Adapters.Persistence + {Name}.Adapters.Presentation — Suitable for medium to large projects
{ProjectRoot}/
├── {name}.slnx # Solution file
├── Directory.Build.props # FunctoriumSrcRoot + common settings
├── Src/
│ ├── {Name}.Domain/ # Domain Layer
│ ├── {Name}.Application/ # Application Layer
│ ├── {Name}.Adapters.Infrastructure/ # Mediator, OpenTelemetry, External Services
│ ├── {Name}.Adapters.Persistence/ # EfCore, Dapper, InMemory
│ ├── {Name}.Adapters.Presentation/ # FastEndpoints
│ └── {Name}/ # Host (Program.cs)
└── Tests/
├── {Name}.Tests.Unit/ # Domain + Application + Architecture
└── {Name}.Tests.Integration/ # HTTP Endpoint E2E
{Name}.Domain/
├── AggregateRoots/
│ └── {Aggregate}/
│ ├── {Aggregate}.cs # AggregateRoot
│ ├── I{Aggregate}Repository.cs # Repository Port
│ ├── ValueObjects/ # VO collection
│ └── Specifications/ # Query conditions
└── SharedModels/
└── Services/ # Domain Service
{Name}.Application/
└── Usecases/
└── {Aggregate}/
├── Commands/ # State-changing use cases
├── Queries/ # Read use cases
├── Events/ # Domain event handlers
├── I{Aggregate}Query.cs # Read Port (list queries)
└── I{Aggregate}DetailQuery.cs # Read Port (detail queries)
{Name}.Adapters.Persistence/
└── {Aggregate}/
├── {Aggregate}.Model.cs # DB POCO model
├── {Aggregate}.Configuration.cs # EF Core Fluent API
├── {Aggregate}.Mapper.cs # Domain <-> Model conversion
├── Repositories/
│ ├── {Aggregate}RepositoryEfCore.cs
│ └── {Aggregate}RepositoryInMemory.cs
└── Queries/
├── {Aggregate}QueryDapper.cs
└── {Aggregate}QueryInMemory.cs

Adapter folders are organized in 3 dimensions:

DimensionRepresentationExample
Aggregate (what)Primary folderProducts/, Orders/
CQRS Role (read/write)Secondary folderRepositories/, Queries/
Technology (how)Class suffixEfCore, InMemory, Dapper
File TypePatternExample
Repository{Aggregate}Repository{Variant}.csProductRepositoryEfCore.cs
Query{Aggregate}Query{Variant}.csProductQueryDapper.cs
DB Model{Aggregate}.Model.csProduct.Model.cs
EF Configuration{Aggregate}.Configuration.csProduct.Configuration.cs
EF Mapper{Aggregate}.Mapper.csProduct.Mapper.cs

Layer dependency direction flows only inward:

Host -> Adapters.Infrastructure -> Application -> Domain
-> Adapters.Persistence -> Application -> Domain
-> Adapters.Presentation -> Application

Domain references only the Functorium framework and SourceGenerators. Application references only Domain. Adapters depend on Application or Domain, but the reverse is not allowed.

Switch between InMemory/Sqlite via Persistence:Provider in appsettings.json:

{
"Persistence": {
"Provider": "InMemory",
"ConnectionString": "Data Source=app.db"
}
}

Branch by Provider in DI registration:

var provider = config["Persistence:Provider"];
return provider switch
{
"Sqlite" => services.RegisterSqliteRepositories(config),
_ => services.RegisterInMemoryRepositories()
};

Configure the OpenTelemetry 3-Pillar (Logging + Tracing + Metrics) pipeline:

services.RegisterOpenTelemetry(config, assembly)
.ConfigurePipelines(p => p
.UseMetrics() // 1. Metrics collection
.UseTracing() // 2. Distributed tracing
.UseCtxEnricher() // 3. Business context propagation
.UseLogging() // 4. Structured logging
.UseException()) // 5. Exception conversion
.Build();

HTTP endpoints are configured based on FastEndpoints.

Leverages advanced features of the IO monad:

PatternPurposeExample
IO.Timeout + CatchTimeout + conditional recoveryHealth check API
IO.Retry + ScheduleExponential backoff retryExternal monitoring API
IO.Fork + AwaitParallel executionParallel compliance checks
IO.BracketResource acquire-use-releaseRegistry session management

All collected decisions are organized into {context}/01-architecture-design.md.

  • Project structure diagram (folder tree)
  • Solution file (.slnx) configuration
  • Project reference relationship diagram
  • Per-layer naming convention table
  • DI registration strategy (roles per Registration class)
  • Persistence Provider switching configuration (appsettings.json)
  • Observability pipeline order
  • Build/test commands

The architecture design is complete.

Next Steps:

  1. Use the domain-develop skill to design and implement each Aggregate in detail
  2. 4-step documents from 00-business-requirements.md -> 03-implementation-results.md will be generated per Aggregate
  • Layer dependency direction: Domain <- Application <- Adapter <- Host
  • Adapter folder organization: Aggregate-centric (primary) + CQRS Role (secondary) + Technology suffix
  • Naming: {Subject}{Role}{Variant} (e.g., ProductRepositoryEfCore)
  • DB model/configuration files: Dot notation (e.g., Product.Model.cs, Product.Configuration.cs)
  • Persistence Provider switching: Switch InMemory/Sqlite via Persistence:Provider in appsettings.json
  • $(FunctoriumSrcRoot) variable: Used for framework references in csproj