Appearance
ADR-001: Microservices Architecture with Clean Architecture per Service
Status
Accepted
Date
2023-Q1
Context
Microtec ERP needed to support a broad scope of business modules: Accounting, HR, Inventory, Sales, Purchase, Finance, Distribution, and more. These modules are developed by different teams, have different scaling requirements, and are released on different cadences.
The initial prototype was a monolithic .NET application. As the feature set grew, the monolith created bottlenecks:
- A bug in Inventory would require redeploying all modules
- The HR team's release schedule blocked the Accounting team
- Scaling the reporting engine for export-heavy operations scaled the entire application
- Database schema changes for one module required careful coordination across all modules
- Growing codebase made onboarding new developers slow
We considered three architectural approaches:
| Option | Description | Key Concerns |
|---|---|---|
| Monolith | Single deployable, shared database | Already hitting limits; scaling/team constraints |
| Modular Monolith | Single deployable, isolated modules | Better, but still coupled deployment |
| Microservices | Independent services per domain | Operational complexity, but maximum flexibility |
Decision
Adopt microservices architecture with Clean Architecture applied independently within each service.
Each business domain (Accounting, HR, Inventory, etc.) is an independent microservice with:
- Its own deployable unit (Docker container)
- Its own database (database-per-tenant, see ADR-003)
- Its own CI/CD pipeline
- Clean Architecture layers: Domain → Application → Infrastructure → API
Cross-cutting concerns (auth, messaging, persistence patterns) are shared via NuGet packages (see ADR-006).
Clean Architecture per Service
{ServiceName}/
├── {ServiceName}.Apis/ # REST controllers, Swagger, DI
├── {ServiceName}.Application/ # CQRS handlers, validators, DTOs
├── {ServiceName}.Domain/ # Entities, domain events, value objects
└── {ServiceName}.Infrastructure/# EF Core, external integrationsCQRS with MediatR
All operations go through MediatR:
- Commands: write operations (
AddInvoiceCommand,EditEmployeeCommand) - Queries: read operations (
GetAllInvoicesQuery,GetByIdEmployeeQuery) - Handlers are co-located with their commands/queries in feature folders
Consequences
Positive
- Independent deployment: Each service deploys independently — a bug in HR doesn't affect Accounting
- Team autonomy: Each team owns their service end-to-end (code, database, deployment)
- Independent scaling: The reporting service can scale separately from the transactional API
- Technology flexibility: Individual services can evolve their stack independently
- Fault isolation: A failure in one service degrades only that domain — not the entire application
- Smaller codebases: Each service is focused and easier to understand
Negative
- Operational complexity: 14+ services to deploy, monitor, and troubleshoot
- Distributed system challenges: Network failures, partial failures, eventual consistency
- Inter-service communication overhead: HTTP calls vs in-process method calls
- Data consistency: Transactions span multiple services — requires saga patterns or eventual consistency
- Higher infrastructure cost: 14+ containers vs 1 monolith
- Onboarding complexity: New developers must understand the service mesh, not just one app
Neutral
- Shared NuGet packages became necessary to avoid code duplication — see ADR-006
- API gateway became necessary for unified entry point — see ADR-007
- Service mesh (mTLS via ACA) became the default — see ADR-002
Implementation Notes
Current Services
| Service | Domain | Solution |
|---|---|---|
AppsPortal.Apis | Accounting (complete) | Microtec.Platforms.sln |
HR.Apis | Human Resources | Microtec.Platforms.sln |
Inventory.Apis | Inventory | Microtec.Platforms.sln |
Sales.Apis | Sales (partial) | Microtec.Platforms.sln |
BusinessOwners.Apis | Tenant admin portal | Microtec.BusinessOwner.sln |
Attachment.Apis | File storage | Infrastructure service |
Notification.Apis | Email/SMS/Push | Infrastructure service |
Workflow.Apis | Business workflows | Infrastructure service |
Integration.Apis | External integrations | Infrastructure service |
Gateway.API | API gateway | Microtec.Platforms.sln |
Naming Convention
- Feature folders:
{Feature}/Commands/,{Feature}/Queries/ - Commands:
Add{Entity},Edit{Entity},Delete{Entity} - Queries:
GetById{Entity},GetAll{Entity},GetDropdown{Entity}
Related ADRs
- ADR-002: Azure Container Apps (runtime for microservices)
- ADR-003: Database-per-tenant (data isolation)
- ADR-005: Azure Service Bus (inter-service messaging)
- ADR-006: NuGet packages (shared cross-cutting concerns)
- ADR-007: YARP gateway (unified entry point)
- ADR-008: Dual-token design (auth for microservices)