DDD架构下,如何合理存储EventType与EventCategory的关联映射逻辑?
Great question—this is a classic DDD scenario where balancing value object purity, domain expressiveness, and infrastructure needs gets tricky. Let’s break this down based on DDD principles and your specific requirements:
First, remember that Value Objects (VOs) should be immutable, self-contained, and focused on descriptive attributes—they shouldn’t hold cross-VO mapping logic or global business rules. That’s a key guardrail we’ll use to evaluate each option.
Option 1: Store Mapping in One of the Value Objects
Let’s say you put the Category → EventType mapping inside EventCategory:
- ❌ Problem: This violates the VO’s single responsibility.
EventCategoryshould be a pure identifier (e.g., "WEATHER", "COMPETITION")—not a rule manager. Tying it to a list ofEventTypes creates tight coupling between the two VOs and makes the VO dependent on external state. - 🤔 Edge Case Exception: If the mapping is an inherent, unchangeable part of the category’s definition (e.g., WeatherCategory will always only include Rain/Sun events), you could add a method like
isValidEventType(EventType type)toEventCategoryinstead of storing the full list. This keeps the VO immutable but encapsulates its own validation rule. However, this still forces the infrastructure layer to depend on the VO’s logic for queries, which can get messy if rules ever need to be adjusted or configured.
Option 2: Store Mapping in the Event Entity
- ❌ Problem: The
Evententity’s job is to represent a single event’s state and enforce its own consistency—not to hold global business rules. Putting a cross-category/event-type mapping here would bloat the entity with logic that doesn’t belong to individual events, violating single responsibility. It also doesn’t make sense for everyEventinstance to carry a copy of this global rule set. - ✅ What You Can Do: Let the
Evententity encapsulate its own consistency check (e.g.,ensureCategoryMatchesEventType()), but have this method depend on an external rule source (not hardcode the mapping inside the entity).
Option 3: Store Mapping in a Dedicated Domain Rule Class/Service (RECOMMENDED)
This aligns best with DDD principles, because:
DDD dictates that cross-entity/VO business rules that don’t belong to a single entity/VO should live in domain services or dedicated rule classes.
How to Implement This:
- Create a domain-level rule class (e.g.,
EventCategoryTypeRegistry)—this is part of your domain layer, not infrastructure. It holds theCategory → EventTypemapping (as aMap<EventCategory, Set<EventType>>or via a method likegetAllowedEventTypes(EventCategory category)). - Use it for entity validation: Instead of an external validator, inject this registry into the
Evententity’s constructor (or use a domain service to createEvents) to validate that the chosenEventTypematches theEventCategoryat creation time. This keeps domain logic internal to the domain layer, where it belongs. - Use it for repository queries: Your infrastructure repository can depend on this registry to translate a
EventCategoryfilter into a list ofEventTypes. For example, when querying forWeatherCategoryevents, the repo callsregistry.getAllowedEventTypes(WeatherCategory)and uses that list to build the query (e.g., SQLINclause). This way, the infrastructure layer doesn’t need to know the mapping rules—it just uses the domain layer’s exposed logic.
Key Benefits:
- 🧹 Keeps VOs and entities pure:
EventTypeandEventCategoryremain simple, immutable descriptors with no cross-dependencies. - 🎯 Centralizes rule management: All mapping logic lives in one place, making it easy to update or even make configurable (e.g., load mappings from a domain config store) without touching entities/VOs.
- 🔄 Supports both validation and query needs: The same rule source powers both entity consistency checks and repository query logic, ensuring alignment across the system.
内容的提问来源于stack exchange,提问作者Bertuz

