Context:
Uncle Bob has an example called the payroll example. This page has a great downloadable example and walkthrough of what this is exactly: http://cleancodejava.com/uncle-bob-payroll-case-study-full-implementation/#disqus_thread.
In the payroll example, an employee has 4 classes that represent pay schedule/pay type/etc.. (which could be from 20 different tables theoretically). There are various "interactors" that interact with each of these classes. The interactors use a gateway to access the employee via findById() which returns a loaded employee with all 4 classes also retrieved from the database.
Question:
This is great in example but in a larger system I run into some questions around entity fetching.
A new use case comes up where we just want to edit the employees name. Using the gateway findById returns an employee (great!) but also does a bunch of unnecessary loading of the 4 extra classes that are unnecessary to the updateEmployeeName interactor.
So:
Or did I just misunderstand what entity means entirely? Is this the reason why in this architecture the employee is abstract? So the gateway can return stripped down versions that still "appear" as an employee?
Thanks a bunch!
Using the gateway findById returns an employee (great!) but also does a bunch of unnecessary loading of the 4 extra classes that are unnecessary
It seems that you are mixing database entities with entities. The entities in the clean architecture are not database entities. You should not have any database details in these entities. This even includes annotations like some frameworks use, e.g. @Entity(table="employee")
Separate the business entity from the database entity. If you do you can decoupled the entities from how they are persisted. In some cases you might use plain sql and in other cases you use a ORM framework. But if you decouple them you can design the database entities according to the persistene needs and the business entities according to business needs. This is also an application of the single responsibility principle since the two change for different reasons.
Do we partially load the employee based on the use case? This means the gateway may have many many more highly specialized loading methods.
Yes you do. But you can also introduce an entity for that type of usage. E.g. an EmployeeUpdate
that only has the properties that can be updated.
Does the entity "lazy load" other fields as necessary? This means the entity would need a gateway to access its sub fields that are in other tables.
The entity should never load any fields. If it does it is not a business entity it's a database entity provided by some kind of ORM framework. You should separate them as I explained above.
Any other ideas on how this may work?
When we load a complete entity to just update a single property we often think about efficiency and performance. But we should also think about the use case itself. Is it executed a lot of times or is it a use case that is only executed a few times. Is the effort to provide an performance optimized rename method worth it or should we just load the whole data?
Finally I would create a repository that returns an EmployeeUpdate
. E.g.
public interface UpdateEmployeeRepository {
public EmployeeUpdate getEmployeeForUpdate(int employeeNumber);
public void applyEmployeeUpdate(EmployeeUpdate eu);
}
As you can see I would also specify a use case specific repository interface. This is also an application of the single responsibility principle.
Maybe this talk from Uncle Bob can help you too.
EDIT
I was wondering if there were entities for specific use cases and it sounds like so! Definitely seems like there could be a lot entity bloat (e.g. 50 different entities that can all update different features etc)
You can see it as different interfaces to the same entity or different roles that an entity plays in different use cases.
For me an interface is like a perspective on one thing. If I hold something in my hand I can turn it around and it will look different from each perspective (unless it is a sphere). The same applies to interfaces. In your use case you look at the Employee
entity from the update perspective.
Chapter 5 Entities of the book Implementing domain-driven-design by Vaughn Vernon might help you too. On page 200 he starts with the section Roles and Responsibilities and describes how Domain Objects Playing Multiple Roles and also discusses pitfalls. E.g. he talks about object schizophrenia. A concept that I was aware of before, but a term that I first read in his book. The book contains code examples in Java and C#. A really great book, because it removes the gap between Eric Evans book Domain Driven Design and implementation issues.