# Domain-Driven Design Reconsidered

Why explicit business boundaries matter more than the next technology stack\

Some architecture decisions look convincing on a whiteboard and still fall apart in production. The microservices are drawn. The APIs are documented. Deployments are automated. Technically, everything appears modern. Yet the business and engineering teams talk about different things, responsibilities remain unclear, and every change triggers a chain of unintended side effects.

When that happens, Kubernetes, the API gateway, or the database are rarely the root cause. The problem started earlier. The system was organized around technical components before the business relationships were properly understood.

This is where Domain-Driven Design begins. Not as a framework. Not as a recipe for microservices. And certainly not as a competition to use as many patterns as possible. DDD starts with a simple but uncomfortable question: Does our software truly understand the business, or does it merely process data structures whose actual meaning lives somewhere outside the code?

Internal Development Platforms and coding agents do not make that question obsolete. They make it more urgent. The faster teams can provision infrastructure and generate code, the more important the business structure becomes that guides this speed.

## **Architecture Does Not Start with Technology**

Domain-Driven Design starts with the problem space. Which capabilities differentiate the business? Which rules must never be violated? Which terms are used by the people who work in the domain every day? And where does a term change its meaning because another business area looks at it from a different perspective?

This sounds obvious. In real organizations, it is not. A product means one thing to sales and another to logistics. An order changes meaning as it moves from sales to payment processing or fulfilment. Trying to force all these perspectives into one enterprise-wide model does not create consistency. It creates ambiguity.

*DDD does not require one model to apply everywhere. It requires one unambiguous model to apply whithin an explicit boundary.*

That boundary is the Bounded Context. Within it, a defined language, a model, and clear responsibilities apply. Relationships between contexts are designed deliberately - for example through published interfaces, Domain Events, or an Anti-Corruption Layer that translates an external model instead of allowing it to leak directly into the local domain.

## **A Shared Language Is Not a Workshop Deliverable**

Ubiquitous Language is one of the best-known DDD concepts and one of the most frequently underestimated. A shared language is not created by placing a glossary in Confluence. It has to be visible in conversations, acceptance criteria, tests, interfaces, and code.

If the business talks about a booking while the code uses BookingRecord, ReservationEntity, OrderDTO, and StayData for the same concept, there is no shared language. There is a translation problem. Every translation increases the risk that business meaning will be lost.

A reliable Ubiquitous Language therefore improves architecture decisions as well. It exposes whether teams are discussing the same concept. It prevents generic operations such as processData or updateStatus when the actual business action is checkInGuest, cancelBooking, or assignRoom. And it forces ambiguity to be resolved where it begins - in the model, not during an incident.

## Strategic and Tactical DDD Belong Together

DDD operates on two levels. Strategic design organizes the domain: subdomains, the Core Domain, Bounded Contexts, and the relationships between them. Tactical design translates the business structure inside a context into code.

The strategic level determines where responsibility starts and ends. The tactical level determines how business rules are modeled so they cannot simply be bypassed. Applying tactical patterns without understanding the business boundaries can produce beautifully structured code in the wrong context. Drawing Context Maps without carrying their insights into the implementation produces the opposite failure: anemic data models and generic services that have lost the business meaning.

**The Essential Tactical Building Blocks**

- Entities have a distinct identity and lifecycle. A booking remains the same booking even when its status or attributes change.
- Value Objects are defined by their values, are immutable, and encapsulate small business rules. A date range, for example, should not exist if its end precedes its start.
- Aggregates form business consistency boundaries. The Aggregate Root is the controlled entry point and protects the invariants inside that boundary.
- Domain Services contain business logic that does not naturally belong to a single Aggregate. They remain stateless and free of infrastructure concerns.
- Application Services orchestrate a use case: load, delegate, persist, and connect to external systems. Business decisions do not belong there.
- Repositories expose Aggregates as domain-oriented collections while hiding the technical persistence implementation.
- Domain Events describe business-relevant facts in the past tense and decouple downstream reactions.
- Read Models optimize the query path without distorting the protected write model for dashboards, reports, or UI requirements.

## A Hotel Check-In Is More Than a Database Update

A practical example makes the difference visible. Checking a guest into a hotel requires a booking and a room to be coordinated. The guest must meet the relevant business requirements. The room must not be double-booked or under maintenance. Once the guest checks in, the booking enters a different stage of its lifecycle; a cancellation that was previously permitted may no longer be allowed.

A CRUD perspective reduces this process to updating several fields. The domain model sees something else: a state transition with invariants, responsibilities, and downstream consequences.

The booking and the room can be modeled as separate Aggregates that reference each other only through typed identifiers. An Application Service receives an explicitly named business Command. A Domain Service coordinates the two Aggregates when the decision genuinely requires knowledge from both consistency boundaries. At check-out, the booking publishes a Domain Event. Listeners can then release the room, initiate billing, or start further processes - decoupled and, where appropriate, in separate transactions.

The important point is not the number of classes. It is that each rule has one clear home. The system does not merely document an invalid state; it actively prevents it.

## DDD Does Not Automatically Mean Microservices

One of the most persistent misconceptions is that a Bounded Context is a microservice. It can become one, but it does not have to. A Bounded Context is first and foremost a business and semantic boundary. Turning it into an independent deployment unit is a later technical decision.

For many organizations, a modular monolith is the better starting point. It enables explicit business modules and shared deployment processes without immediately accepting the operational cost of a distributed system. A business boundary may become a physical service boundary when independent scaling, different release cadences, team autonomy, or technical isolation genuinely justify it.

*Cut the domain first. Discuss deployment boundaries afterwards, not the other way around.*

Cloud design patterns should follow the same logic. Choose them because they address a specific problem, not because a technology looks attractive. An Anti-Corruption Layer protects a modern model from a legacy system. Choreography decouples downstream business processes. Compensating Transactions address multi-step processes in eventually consistent systems. The pattern follows the problem, not the cloud provider’s product catalogue.

## The Internal Development Platform Makes DDD Deliverable

DDD creates business clarity. An Internal Development Platform creates the operational environment in which autonomous teams can deliver that clarity reliably. The approaches solve different problems, which is precisely why they complement each other.

An IDP is not a loose collection of tools and not simply a portal with a few buttons. It is an internal product for engineering teams. It standardizes recurring platform capabilities, automates guardrails, and provides secure self-service paths without forcing every team through the same manual coordination with infrastructure, security, and operations groups.

**Building Blocks of a DDD-Ready Platform**

- A self-service developer portal provides standardized paths for services, databases, messaging, secrets, and runtime environments.
- Infrastructure as Code creates reproducible, version-controlled infrastructure that can be modularized around domain needs.
- CI/CD pipelines automate build, test, and deployment. Contract tests can help protect published boundaries between contexts.
- An API gateway and, where needed, a service mesh secure and observe communication between services and contexts.
- Event streaming supports asynchronous exchange of Domain Events and reduces temporal coupling.
- Monitoring and alerting create visibility into technical conditions and, where meaningful, domain-specific workflows.
- Security and governance become automated platform capabilities, including identities, roles, policies, and compliance checks.
- FinOps integrates budgets, cost reporting, and alerts into engineering and operations.

On Azure, such a platform can use Azure DevOps or GitHub Actions for pipelines, Terraform for Infrastructure as Code, AKS as the container platform, Azure API Management for interfaces, Azure Event Hubs or Service Bus for asynchronous communication, Azure Monitor and Application Insights for observability, and Microsoft Entra ID for authentication and role-based access. Azure Cost Management can add budgets and cost alerts. These are building blocks. The platform product emerges from a consistent developer experience and carefully designed guardrails.

The architectural point matters: the platform must not replace domain boundaries. It should support them. Templates, golden paths, and policies should accelerate teams without forcing every Bounded Context into the same technical mold. Too much freedom creates fragmentation. Too much governance creates centralized platform bureaucracy. Finding the balance is a product decision, not a one-time infrastructure exercise.

## Coding Agents Change the Economics—not the Responsibility

Coding agents are changing the distribution of work. Boilerplate, technical scaffolding, and initial implementation proposals can be created much faster. What they do not create automatically is a sound business design. Without explicit direction, the agent fills the design vacuum with a statistically plausible structure. That structure may compile, pass tests, and still be wrong for the domain.

This is why tactical DDD becomes especially valuable in AI-assisted engineering. Aggregates constrain the relevant context. Value Objects move rules into the type system. Intention-revealing interfaces and Commands make business language machine-readable. Domain Events establish explicit extension points. Separate read and write paths prevent a short-term UI request from gradually corrupting the domain model.

The code remains the runtime reality. A specification describes what should be built. The executable code shows what was actually built. Tests are an essential bridge, but they do not replace good design. High test coverage cannot repair incorrect responsibility boundaries or a Big Ball of Mud.

## Software Design as a Persistent Instruction

Recurring architecture rules should not be rewritten in every prompt. They belong in persistent project context—for example an [agent.md](http://agent.md), [CLAUDE.md](http://CLAUDE.md), or a comparable project file, depending on the tool. This is where stable rules live: Aggregates protect their invariants, Value Objects are immutable, Repositories return complete Aggregates, and Domain Events carry minimal data and use the past tense.

The individual assignment can then remain short and business-focused. It identifies the affected domain objects, invariants, causal chain, and intended location of the logic. The agent performs the syntactic implementation within a known design. The human retains design authority. Marker interfaces or comparable metadata can make that structure machine-readable as well. They allow diagrams to be derived from the actual codebase: Which Aggregates exist? Which Events are published? Which Services coordinate which parts of the domain? A diagram generated from code is not a competing truth. It is a current view of the truth implemented in code.

## A Practical End-to-End Approach

DDD, platform engineering, and AI-assisted development work together when they are treated as one operating model rather than three parallel initiatives.

1. Explore the domain together. Make business events, actors, rules, and conflicts visible through techniques such as Event Storming or Domain Storytelling.
2. Distinguish Core, Supporting, and Generic Subdomains. Invest the greatest modeling effort where business differentiation and protected rules live.
3. Define Bounded Contexts and their relationships. Use a Context Map to expose dependencies, translation boundaries, and integration patterns.
4. Model tactically inside each relevant context. Keep Aggregates small, protect invariants in the model, and carry business language into methods and types.
5. Choose the deployment and integration model deliberately. A modular monolith, Self-Contained Systems, and microservices are options—not DDD obligations.
6. Provide platform capabilities as golden paths. Standardize infrastructure, pipelines, security, observability, eventing, and cost controls without centralizing the domain.
7. Persist design rules for coding agents and inspect the structural result. Specifications describe intent, the agent implements it, and tests plus code-derived views support the review.

Start deliberately small. One meaningful domain, one clearly bounded use case, and one team are enough to validate the working model, platform capabilities, and review mechanisms. An enterprise-wide big bang contradicts the evolutionary nature of DDD.

## When the Investment Is Not Justified

DDD is not a universal badge of quality. Simple CRUD applications, short-lived prototypes, purely technical utilities, or data-centric systems without rich business rules often do not benefit from the full tactical toolkit. A straightforward layered architecture or data-oriented approach may be more economical.

An IDP is not automatically the right answer for every organization either. Building one is demanding. It requires product thinking, platform expertise, and continuous maintenance. Cloud-specific components can create lock-in. Governance can empower teams or obstruct them. And without cultural change, self-service quickly becomes just another centralized request queue.

The right question is therefore not whether to adopt DDD, microservices, an IDP, or coding agents. The right question is: What kind of complexity are we dealing with and which boundaries, tools, and responsibilities will help us control it over time?

## My Conclusion

Domain-Driven Design remains one of the strongest approaches for connecting business knowledge, organizational responsibility, and software architecture. Its value is not measured by the number of patterns used. Its value lies in the clarity it creates: a shared language, visible responsibility boundaries, protected business rules, and deliberately designed relationships.

An Internal Development Platform turns that clarity into repeatable delivery capabilities. It gives teams secure paths to build and operate their contexts autonomously. Coding agents increase the speed inside this system - but they do not remove the need for guardrails. They make those guardrails more important.

*Speed is only an advantage when the direction is right. DDD provides the business compass. The platform builds the road. The coding agent drives faster - but architecture still decides where it goes.*

Organizations that select the technology first and then force the domain into it will reproduce old problems at a higher speed. Organizations that take business language, boundaries, and invariants seriously create an architecture that does not merely work today, but can still be understood, changed, and responsibly operated tomorrow.
