Clean Architecture
Rapid overview
Clean Architecture - Layered Design for Trading Systems
Keep business rules independent of frameworks, UI, and data stores by forcing dependencies inward.
---
Quick Overview
- Domain: Entities, value objects, aggregates, invariants. No external dependencies.
- Application: Use cases, CQRS handlers, validators, interfaces.
- Infrastructure: Databases, MT4/MT5 bridges, message brokers, external APIs.
- Presentation: Controllers, endpoints, UI components.
- Rule: Dependencies always point inward.
---
Detailed Explanation
Layer Responsibilities
+-------------------------------+
| Presentation | Controllers, endpoints
+-------------------------------+
| Application | Use cases, CQRS
+-------------------------------+
| Domain | Entities, invariants
+-------------------------------+
| Infrastructure | DBs, brokers, APIs
+-------------------------------+
Dependency Flow
Presentation -> Application -> Domain
Infrastructure -> Application (via interfaces)
Example in a Trading Context
Domain layer (core rules):
public sealed class Order
{
public Guid Id { get; } = Guid.NewGuid();
public string Symbol { get; private set; }
public decimal Quantity { get; private set; }
private Order() { }
public static Order Create(string symbol, decimal quantity)
{
if (string.IsNullOrWhiteSpace(symbol))
throw new ArgumentException("Symbol is required", nameof(symbol));
if (quantity <= 0)
throw new ArgumentException("Quantity must be positive", nameof(quantity));
return new Order { Symbol = symbol.ToUpperInvariant(), Quantity = quantity };
}
}
Application layer (use case):
public sealed record PlaceOrderCommand(string Symbol, decimal Quantity) : IRequest<Guid>;
public sealed class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, Guid>
{
private readonly IOrderRepository _orders;
public PlaceOrderHandler(IOrderRepository orders)
{
_orders = orders;
}
public async Task<Guid> Handle(PlaceOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.Symbol, request.Quantity);
await _orders.AddAsync(order, ct);
return order.Id;
}
}
Infrastructure layer (implementation):
public sealed class SqlOrderRepository : IOrderRepository
{
private readonly TradingDbContext _db;
public SqlOrderRepository(TradingDbContext db)
{
_db = db;
}
public async Task AddAsync(Order order, CancellationToken ct)
{
_db.Orders.Add(order);
await _db.SaveChangesAsync(ct);
}
}
Presentation layer (API):
[ApiController]
[Route("api/orders")]
public sealed class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
public OrdersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] PlaceOrderCommand command, CancellationToken ct)
{
var orderId = await _mediator.Send(command, ct);
return Ok(new { orderId });
}
}
---
Why It Matters for Interviews
- You can explain how dependencies flow and why it protects business rules.
- You can show testability by mocking infrastructure in application tests.
- You can link architecture to maintainability in trading systems.
---
Common Pitfalls
- Putting database or HTTP code in the Domain layer.
- Over-abstracting everything before the domain is understood.
- Letting DTOs and EF Core entities leak into the Domain.
- Ignoring use case boundaries and building anemic services.
---
Quick Reference
- Domain: No dependencies, enforce invariants.
- Application: Use cases, interfaces, orchestration.
- Infrastructure: Implement interfaces, handle IO.
- Presentation: HTTP/UI, composition root.
---
Sample Interview Q&A
- Q: Where do CQRS handlers live?
- A: In the Application layer because they orchestrate use cases.
- Q: How do you keep Infrastructure replaceable?
- A: Define interfaces in Application/Domain, implement in Infrastructure, and wire with DI.
- Q: How does Clean Architecture relate to DDD and vertical slices?
- A: DDD shapes the Domain model, while vertical slices organize Application use cases. Clean Architecture defines dependency flow around them.
- Q: When would you relax strict layering?
- A: Only when performance profiling proves a bottleneck and the decision is documented.