Many systems exist to change the world’s state, so handling mutability safely is central to secure design. This chapter promotes modeling change as Domain-Driven Design entities—objects with identity whose state evolves under clear, enforced rules. Scattering state across cookies, direct SQL, and ad hoc application code creates “logical cracks” where security failures appear. By concentrating allowed states and transitions inside entities, the model stays comprehensible, constraints become explicit, and security boundaries are easier to defend.
Security begins at creation: entities must be consistent the moment they come into existence. The chapter warns against no-argument constructors and setter-based initialization, which routinely yield partially formed, rule-violating objects and brittle code. With ORMs, prefer private no-arg constructors and field access to avoid exposing unsafe creation paths, or separate persistence and domain models entirely. For construction, start with constructors that require all mandatory attributes and validate inputs; use fluent interfaces for readability when adding optional attributes; and enforce cross-attribute invariants with explicit checks. For advanced constraints and multistep assembly, employ the builder pattern to hide half-built objects and release them only after passing invariant checks; when mapping via ORM, run invariant validation after loading data.
After creation, preserve integrity by preventing external mutation. Replace broad getters/setters with behavior-oriented methods that encode the allowed transitions (for example, markPaid rather than setPaid). Prefer immutable domain primitives for attributes, parameters, and return values; where mutability is unavoidable, use defensive copies to avoid leaking internal state. Collections need special care: keep list/set operations inside the entity, expose read-only copies or unmodifiable views, and make contained items immutable to prevent indirect tampering. Applied together, these practices keep entities consistent, encapsulate business rules at the source of truth, and close the security gaps that arise when mutable state leaks beyond the model’s control.
Figure 6.1. Some different ways to implement state
Figure 6.2. We don’t want half-finished cars to be visible to the outside.
Figure 6.3. Safeguarding a bank account by either a credit limit or a fallback account
Figure 6.4. Hiding a half-finished car until it’s finished
Summary
- Entities are the preferred way to handle mutable states.
- Entities must be consistent on creation.
- No-arg constructors are dangerous.
- The builder pattern can be used to construct entities with complex constraints.
- You need to protect the integrity of attributes when those are accessed.
- A private data field with unrestricted getter and setter methods isn’t more secure than a public data field.
- You should avoid sharing mutable objects and use immutable domain primitives instead.
- You shouldn’t expose a collection, but rather expose a useful property of the collection.
- Collections can be protected by exposing an unmodifiable version.
- You must take care so that the data in a collection can’t be changed from the outside.
FAQ
Why model mutable state as entities in the DDD style?
Entities centralize both state and the rules for changing it. That makes allowed states and transitions explicit, reduces the chance of security gaps caused by scattered logic (cookies, direct SQL, random services), and keeps business invariants close to the data they govern.What’s wrong with no-arg constructors and setter-based initialization?
No-arg constructors allow half-baked objects to escape, and setter sequences are easy to forget or apply inconsistently. This leads to entities created in invalid states, subtle bugs, and security loopholes that compilers and basic tests won’t catch.How can I use JPA/Hibernate without weakening entity integrity?
You have two safe options: (1) Separate domain and persistence models, mapping DB rows to persistence classes and then constructing domain entities safely; or (2) keep a private no-arg constructor and use field access (not setters) so the ORM can hydrate objects without exposing inconsistent instances to callers.When should I use constructors, fluent interfaces, or the builder pattern?
- Mandatory-attribute constructor: for simple cases where only required fields must be set at creation.- Fluent interface: for readability while adding optional attributes, provided each step leaves the object consistent.
- Builder pattern: for complex, multi-step construction and cross-field constraints that must hold before exposure.
Secure by Design ebook for free