DDD in an LDAP web console: what worked, what was cut, and why tests guard layers
Axia is an open-source .NET 10 web console and REST API for Active Directory and OpenLDAP. Its author explains why half of the initial DDD design was dropped after several security reviews. The article covers what paid off, including value objects, ports and adapters, and architecture tests, and what was replaced, such as domain-event auditing.
Key points
- Separate data you own from data you only read and write in an external system; use aggregates only for the former.
- Model search filters, DNs and secrets as value objects, and make ToString() hide secrets to prevent leaks.
- Enforce audit logging with a MediatR pipeline behavior and a marker interface instead of domain events.
- Keep command and handler types internal and verify layer boundaries with reflection-based tests.
- Map roles to groups by DN rather than CN, because CN is only unique within its container.
- If an authorization check needs external queries, place it in the Application layer and cover it with tests.
The core problem was that the application does not own its data. Users and groups live in Active Directory or OpenLDAP, so a rich User aggregate with invariants could contradict the directory's own rules. The author therefore models users as a read-only record, and reserves value objects for values that carry real meaning, such as search filters, distinguished names and encrypted secrets.
Audit moved from domain events to a MediatR pipeline behavior driven by a marker interface, because forgetting to publish an event left many operations unlogged. Handlers and commands became internal, and architecture rules are enforced by reflection-based tests rather than convention. The most important authorization rule stayed in the Application layer, since checking it needs calls to external systems that a pure domain cannot make.
Real-world use
- A team building an admin panel on Active Directory or OpenLDAP can keep user data as a read-only model instead of a rich aggregate.
- A MediatR-based application that needs complete audit trails can require every command to declare its audit intent, enforced by a test.
- An app supporting several directory servers can hide each provider behind a port, so the application layer never knows which backend it uses.
Why it matters
Copying an external system's rules into your domain can produce wrong results, so first decide which data you truly own. Cross-cutting concerns like authorization and auditing should be enforced by mechanisms that cannot be forgotten, such as pipelines and architecture tests.
Related stories
- A secure enterprise chat with React, .NET 10 and Microsoft FoundryDEV Community: #dotnet1 d ago
- ASP.NET Core Authentication & Authorization Explained: What to Use, When, and How to Ship It SafelyDEV Community: #aspnetcore1 d ago
- Mastering Rate Limiting and Resource Governance for MCP Servers at ScaleDEV Community: #aspnetcore1 d ago