Notes on Deep vs. Shallow Modeling and Domain-Driven Design
Intermission: The Anti-Hamlet
Chapter 2 focuses on the hazards of too-shallow modeling and what deep modeling feels like, plus how deep modeling mitigates risk.
Key themes:
The risks of incomplete design decisions
The difference between modeling to fit code vs modeling to understand the domain
Security flaws that arise from broken business integrity
How explicit, domain-focused design can reduce risk over time
Data integrity
Definition of data integrity (from the security angle): data hasn’t changed or isn’t generated in an unauthorized manner.
Typical technical approaches to integrity include:
Checksums
Cryptographic signatures
Integrity is about ensuring changes occur according to rules, not arbitrary edits.
Shallow Modeling
Shallow modeling results when modeling stops at the first fitting model without deeper digging or planning.
Design (from Chapter 1) is all active decisions in software development: the explicit choices you make.
In shallow modeling, a design might include treating a quantity as an unrestricted integer without deeper domain constraints.
The core mindset in shallow modeling: focus on representing things in code using language primitives (e.g., integers, floats, booleans, strings) rather than understanding the domain concepts.
The goal becomes: “How can I represent this?” rather than: “What does this concept mean in the domain?”
How shallow models emerge
A running example shows how a conversation can lead to shallow concepts:
Sal:
Adds books to the order.
Deve:
How do we describe a book?
Sal:
A book has a title and a price.
Deve:
Price must be a number; can a book be priced like $19.50 tax excluded?
Sal:
Yes, a book can be $19.50 tax excluded.
Deve concludes:
A book has a title and a price. The title is a string; the price is a float (not an int).
Warning: Never, ever, ever represent money as a float!
Further elaboration:
Books also have an ISBN to separate hardbacks and paperbacks.
Example list: Moby Dick, Pride and Prejudice, Hamlet, Moby Dick again, 1984, and Moby Dick again.
Practical rule: we would say three Moby Dick books (not caring about the purchase order).
Conclusion: Price represented as a float; quantity is treated as an int; both appear as primitive types.
Warning: Do not stop at the claim “It’s an integer” for quantity; the domain may impose more structure.
Shallow model design details
Deve creates a class Book with attributes: title, isbn, price.
Order class gains a method: addOrderLine(Book book, int quantity).
Because order, order line, and book have explicit representations, they become classes.
The type system enforces that these are used in the proper places; passing non-Book where a Book is expected yields a compilation error.
However, quantity remains implicitly represented by the primitive type int.
The type system enforces what it can, but it cannot enforce that the quantity represents a valid domain quantity.
Shortcomings of shallow modeling
In the conversation, Deve assumes title and ISBN are simple strings and uses String types for them.
De facto assumptions:
Title is just a string; ISBN is just a string.
Price is a float (and subject to the usual floating-point issues).
Shallow modeling often leads to implicit representations of important business concepts as primitives (e.g., int, float, string, boolean).
Examples of implicit concepts represented as strings that commonly appear in real systems:
Credit card numbers stored as strings
Social Security numbers (SSNs) stored as strings
Consequences: reduced expressiveness, weaker domain constraints, and greater risk of security vulnerabilities.
Deep Modeling
Deep modeling is a conscious effort to understand the domain beyond code-centric thinking.
The drive is not:
“How do I code this concept?”
But: “How can I understand this concept?”
This leads to deeper dialogues during modeling and an iterative loop of discussions and coding.
The result is the discovery of more domain concepts that should be represented explicitly to capture the full model.
Make the implicit explicit
Core guidance: make implicit concepts explicit when they appear in the domain story.
If an implicit concept like “quantity” emerges, take time to discuss it more deeply.
If it’s interesting enough, make it an explicit concept (e.g., a class) with its own constraints.
Benefits of explicit concepts:
Quantity shows up as its own class with its own rules, constraints, and invariants.
The rest of the code becomes more expressive and robust.
Make the implicit explicit continued
Shallow modeling is a missed opportunity for learning and a potential source of security vulnerabilities.
To seize the opportunity, ask questions like:
What do you mean by quantity?
Can there be variants of quantity?
Are there restrictions?
Through such questions you might learn domain constraints, such as:
A quantity of books can never be negative.
It might not be valid to have zero quantity when there are no books in the order (i.e., there’s no quantity in such cases).
A domain-aware design is more expressive, more robust, and less prone to security vulnerabilities.
The authors mention they will elaborate on design guidelines and security considerations in the upcoming chapters, starting with Domain-Driven Design concepts.
Domain-Driven Design (DDD) foundations preview
The text signals that next chapters will delve into concepts of Domain-Driven Design (DDD) and why they are useful for building robust systems.
The emphasis is on aligning the design with the domain and the business rules rather than solely with programming abstractions.
Summary
Incomplete, missing, or shallow modeling leads to design flaws and security vulnerabilities.
A security flaw in the form of broken business integrity can persist in production and bleed money from the enterprise.
Conscious, explicit design results in a much more robust solution.
The path to robustness involves making implicit domain concepts explicit (e.g., turning quantity into its own class with constraints) and applying domain-driven thinking to structure models accordingly.
Practical notes and formulas
Example calculation for a single line item:
Let the unit price be p and the quantity be q. For example, if p = 19.50 and q = 3:
The line total is calculated as: \text{line_total} = q \times p = 3 \times 19.50 = 58.50
Guidance for money representation (to avoid floating-point issues):
It's best practice to represent money using integers in the smallest currency unit (e.g., cents for USD) to prevent precision errors common with floating-point numbers.
If the price in cents is denoted as \text{price_cents} \in \mathbb{Z}, then the price in dollars is \text{price} = \frac{\text{price_cents}}{100}.
Using this method, the line total in cents is calculated as: \text{line_total_cents} = q \times \text{price_cents}
Example for calculating the total of an entire order:
If an order has multiple line items, the order total is the sum of all individual line item totals.
This can be expressed as: \text{order_total} = \sum{i} (qi \cdot pi) where qi is the quantity for item i and p_i is the unit price for item i.
Practical implications: Modeling money and quantities with domain-aware types not only helps avoid calculation errors and improves security, but also aligns software development more closely with specific business rules (such as preventing negative quantities or properly handling tax considerations).
Illustrative examples from the transcript
Book example (attributes): title, isbn, price
Price example: $19.50 (tax excluded)
Quantity: treated as an integer, but not constrained by the type system to reflect domain rules
Real-world caution: using raw strings and primitives for domain concepts leads to loss of expressiveness and potential security gaps
Connections to broader themes
Links to Chapter 1: design consists of active decisions in software development.
Links to real-world practice: many legacy systems rely on primitives for core domain concepts, which can mask important invariants and domain rules.
Ethical/practical implication: explicit, domain-driven design helps ensure data integrity, reduces risk of financial loss due to design flaws, and supports better security posture.
Key terms to remember
Data integrity: assurance that data hasn’t been altered or forged outside allowed rules.
Shallow modeling: stopping at an initial, convenient model that fits code-level representations.
Deep modeling: iterative, domain-focused modeling that reveals explicit domain concepts.
Domain-Driven Design (DDD): design philosophy that emphasizes aligning software models with deep domain understanding.
Explicit design: making latent domain concepts into explicit, constrained models (e.g., a Quantity class).
Security implications: how modeling choices influence vulnerabilities and data integrity in production.
Money representations in software: preference for integer cents over floating-point dollars to avoid precision errors.