Hi OpenRemote Team,
Our Iowa State student group for our software architecture course has been reviewing the use of DDD (Domain-Driven Design) throughout OpenRemote’s codebase.
Something that’s caught our attention is the User entity. Specifically, how it manages user attributes through the List<UserAttribute> collection. While the class effectively serves as an aggregate root by encapsulating identity and user information, we noticed some aspects of its design that might be refined to align more closely with DDD principles. We wanted to get your opinion on a potential refactor to address these areas.
Currently, the User class directly exposes and modifies a list of UserAttribute objects, which is evident in the setAttribute() method and several others:
We believe there are a couple aspects of this approach that could be improved:
- Encapsulation of business rules: Currently, responsibilities like ensuring keys are unique, not null, or properly formatted are managed directly within the entity. Centralizing these validations could enhance clarity and maintainability.
- Coupling of structure and behavior: The close link between the entity’s structure and its behavior could be refined. Using value objects to enforce rules might help achieve greater consistency and modularity.
In Domain-Driven Design, an aggregate root is supposed to protect its rules and manage how other parts of the code interact with its internal data. Directly exposing and modifying raw lists goes against that idea and weakens the integrity of the model.
An alternative approach could be to encapsulate the list of UserAttribute objects inside a dedicated class called UserAttributes. By doing this, you keep all the attribute related logic in one place. It makes validation easier, keeps the code more organized, and gives you a simple, consistent way to work with attributes.
These screenshots give an idea of the refactor.
Refactored User class:
New UserAttributes class:
This refactor has several benefits. First, it encapsulates the internal structure of the attributes, so the aggregate interacts with them through a clear and well-defined interface. It also allows attribute related rules to be enforced consistently in one place, such as preventing duplicates, disallowing null keys, or validating formats. Additionally, the UserAttributes class becomes reusable across other entities if needed. Finally, it makes the code easier to maintain, test, and reason about, while also following DDD principles.
We’d greatly appreciate feedback on whether this change aligns with the project’s direction or if the current approach is preferred. We could also implement a PR if the changes were easier to evaluate there.
Thanks!
Best,
Nate Couture, Patrick Lenahan, Diego Cardona Ortiz, Anthony Phan, Cameron Schweder