At a Learning Circle at Improving, Eric and I asked the group what comes to mind when they hear “Domain-Driven Design.” People mentioned shared language, bounded contexts, aggregates, value objects, event storming, and Entity Framework. Using an ORM does not, by itself, mean you are doing DDD.

Entity Framework can map a domain model to relational tables in many ways. Its mappings do not tell us whether the model reflects the business. DDD begins with the business and the people who understand it.

Business concepts and database tables

I remember approaching software from a data-first background. In my early FoxPro work (dBase and Clipper before that), I thought first about the database when it came to implementation (fortunately, I was already following a human-centric approach to think through what the database should support). Over time, I learned that real-world concepts and how we store their information are separate concerns. A customer in the business might be represented by several tables, or by a collection of information that does not map neatly to one table at all.

That distinction is part of what drew me to Domain-Driven Design. Eric Evans’s book gave names and structure to patterns many of us had already encountered: entities, repositories, aggregates, and value objects. Those tactical patterns matter, and so do the strategic ideas. They help us talk about the business, including where one model’s meaning ends and another begins.

For a broader introduction to those ideas, see Domain-Driven Design Basics for Every Developer, including why bounded contexts matter to front-end developers too.

Take the word order. A sales order means one thing to a salesperson or accounts receivable. A purchase order means something else to accounts payable. The warehouse may care about yet another view. One universal Order class can hide those differences. Bounded contexts give us a way to model each meaning where it belongs.

Pasted image 20261011145810.png

For another example of finding a domain hidden beneath a highly configurable system, see There Is No Domain (Yes, There Is): Pipeline Inspections and the Dynamic App.

Ask what the business is doing

I saw this in a past inventory project. One table, AdjustmentReasons, had only a few fields, including a description and a posting account. We could not use an adjustment reason until its posting account was set. Warehouse staff knew when product was damaged; accounting knew which account should be used.

I wrote about the same scenario in Four Fields, Two Departments, One Bottleneck, including how the warehouse and accounting teams split ownership of that information.

Why make a warehouse worker stop and call accounting before recording a damaged case? Accounting might not need the posting account until closing the books, sometimes weeks later. The person on the warehouse floor knows what happened now: which lot was affected, what they saw, and how many cases appeared damaged.

We asked who needed what information and when. Warehouse staff could record the adjustment with the information they had. Accounting could be notified and provide the posting account before it was needed for the books. Fulfillment needs to know whether an order can be completed; buyers may need to replenish stock.

The table had only a few fields. The business process involved several people, different responsibilities, and different timing. Treating it as “just a table” would have hidden the decisions the software needed to support.

Pasted image 20261011145945.png

Capture what people know when it happens

At each step, I want to know what the person knows at that moment.

A warehouse worker may see two damaged cases but have no idea how much inventory remains. Someone else may find a box of product with no clear explanation for how it got there. Another person may count five cases where the system says there should be twenty-five. Those are different situations. We should capture what the person can report, rather than force them to supply information they do not have.

The worker is reporting an inventory count: the system says 25, but only five are there. That gives us the event, the people involved, and the information available at the time.

The accountant’s later correction is another action, with its own information and responsibility. Modeling the workflow this way lets people do their work without making one team wait on information that is not yet available.

If reporting damage means stopping work to find someone in accounting and get a number, a warehouse worker may skip the report. Let people record the facts they have. The software should fit the work they do.

Shared language helps the whole team

I use ubiquitous language to mean the shared language between the people who understand the business and the people building the software. The group should be able to use a term and understand what it means in the context they are discussing.

That language can show up in user stories, acceptance criteria, tests, and code. A test that says “when a customer has outstanding payments, block new orders” communicates more than a query against a SalesOrders table. The behavior and the business reason are visible. The code tells us how the system implements it.

Behavior-driven development carries that shared language into scenarios and tests. Starting with the behavior helps us avoid designing around a method or database schema before we understand the outcome. Then the implementation can follow the business concepts the team agreed on.

I see the same principle when I use AI to write tests. A Given/When/Then test can still drift into technical details, such as “Given the customer table is populated.” That looks like BDD, but it misses the shared language. When tests and code already express business behavior clearly, AI has examples to follow and can trace related behavior. I still review what it produces, but clear language makes that collaboration more useful.

Design for what we know, then keep learning

We build the best model we can with what we know, then revise it as people use the system. New situations may reveal information we did not have or distinctions we did not understand.

New information does not always require a rewrite. First, ask how the need fits the model we have, what information was available at the time, and what needs to change going forward. Sometimes we can add behavior or capture a fact from now on, while acknowledging that older records never contained it.

We used several event-related terms in the same conversation, but they mean different things. Event storming is a facilitation technique for exploring what happens in a business process and who is involved. Event sourcing builds an operational model from stored events. Event-driven architecture and event streaming describe other ideas. They are related in some systems, but they are not interchangeable, and DDD does not require using all of them.

When I sit with stakeholders, I start with questions: What happened? Who was involved? What did they know? Who needs that information, and when? We may not leave the first conversation with every answer. That is fine. The next good question is often more valuable than a premature design.

I want to understand what a damaged case means to the people handling it before I decide how the system should represent it.

ddd-tlc-post.png

Leave a Reply

Trending

Discover more from Claudio Lassala's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading