10 ms·
This design puts entities in the middle - everything else pivots around entities. Additionally, it uses the OO approach of implementing logic as methods on the
by barrkel 9y ago
This design puts entities in the middle - everything else pivots around entities. Additionally, it uses the OO approach of implementing logic as methods on the entities.
After a couple of decades of experience, I've come to the conclusion that this isn't right. Most business rules involve processes, which are inherently procedural, or, from another perspective, functional - functions of the whole state of the system to a new state of the system. Most processes don't logically belong on an object, and when you stick them to one end, you create lots of problems for yourself.
Just take that example Student entity class; what stops you from writing:
student.RegisteredCourses.Add(course);
Moreover, when you have a relation between two entities, it's not uncommon for the relation to be modelled at both ends; that is, a student has a list of courses, and a course has a list of students. Do you implement the same validation at both ends? Make one end (choose one) read-only? Do edits done on one end automatically turn up on the other end? How do you protect the visibility of methods that keep either end in sync, without exposing invariant-violating APIs to other code?
Invariants and validations for fields are trivial with an OO approach; for entities, they're reasonably easy, but sometimes you need partially invalid versions while editing or constructing an entity; but once you bring in multiple entities, and relations, everything starts getting hairy pretty quickly. Assertions and validations that would be trivial to write [1] in a relational language like SQL aren't possible in an object-oriented language without a lot of system-building.
So I've come around to the idea that the database is a better thing to put at the centre; that encapsulation and hiding of the main fact store is harmful to the architecture of a system, especially in a heterogeneous environment where your entities are represented in different languages, all backed by the same fact store.
[1] Trivial to write, but not necessarily cheap to evaluate. I'm not advocating writing all global validations in SQL.
- UK-AL 9y agoThis is solved by DDD and aggregates. Here's how i would do it. Course - Aggregate Root Student - Aggregate Root RegisteredCourses[RegisteredCourse[]) - Sub Entity Collection - with id reference to course. With metadata like date time when the registration occurred. RegisterForCouse Method There should be no mutable public properties, internal methods should be private. Everything should go through an aggregate root method.
- mbrock 9y agoHow did you decide that the registration is a property of the student and not the course?
- UK-AL 9y agoThis is actually a part of understanding the specific domain. How do the educational practitioners think about the problem for application your working on? Is the application student or course centric? This is big part of DDD, and involves working domain experts and getting inside each others heads. You could defiantly model it as the Register method on the course. But this example shows how you can avoid people messing around with internals once you've modeled it. There is only one way of registering for a course.
- barrkel 9y agoI know there are ways to solve the problem; but I believe these are accidental complexities, not essential complexities. That is, these design solutions complect the problem, sacrificing simplicity for a dubious principle.
- UK-AL 9y agoActually I think this is a major advantage. They are not complexities, they are putting down into code how you think about your application in your head. If your design is some data, which you can mutate however you want to get the job done, you don't really have model of the application. Your business rules can become inconsistent because different developers are implementing different but similar methods in your business logic layer all applying slightly different rules. If the entities themselves enforce these rules, it become a lot less likely.
- mbrock 9y agoI wonder how to even formulate the question to the practitioners -- it seems like an artificial choice imposed by the computer formalism (single-dispatch OO). In other words, I don't see how it is a domain question, and it seems likely that the domain practitioners just think "there are courses, and there are students, and students can be registered for courses". My suspicion is that DDD would basically be better without the focus on single dispatch OO.
- TekMol 9y agoI've come around to the idea that the database is a better thing to put at the centre Same for me. I'm not advocating writing all global validations in SQL. What do you mean? Can you give an example?
- chii 9y agoif only you've gone one step further, and discuss the (imho) cleanest solution - a event store and a query mechanism (aka, event sourcing, and CQRS https://martinfowler.com/bliki/CQRS.html https://martinfowler.com/bliki/CQRS.html). When you want to ask questions about the state of the app, you make queries. These queries could be sql - in which case, your app is directly dependent on the type of storage, and is almost un-abstractable. it could be a custom/bespoke query language (where a set of hardcoded api calls to the Db/datastore counts as an api). The event source system is responsible only for storing facts. Therefore, the "problem" of where the validation of students against courses doesn't exist, because that relationship is a "fact" in the event store (there must've been a registration happening at some point for this fact to exist). Therefore, a programmer _cannot_ make the mistake of accidentally adding a course to a student who didn't register, unless they maliciously do it.
- UK-AL 9y agoMost CQRS/Event store systems, simply just build up OO style domain objects by applying events to them. Then apply a command to the domain object, then save the emitted events. They are still entity centric.
- daigoba66 9y agoI've come to the exact same conclusion after spending time with these same types of systems. Practically speaking, I find that abstracting a system into a set of commands and a set of queries (i.e. CQRS) is often the "right" solution. Each command/query encapsulates an individual use-case, and all of the business rules and database access required.
- UK-AL 9y agoCQRS and entity central solutions normally go together like peas in pod. Command side has a domain model, which would be your business objects in this case. A command simply loads the entity, performs a method on it, saves it. Query has a query model which designed for fast reads, normally built by events emitted by the domain model.
- gloverkcn 9y agoI agree. After 20 years of experience I realize "Clean Architecture" will break down on larger systems. It's easy to create a clean looking architecture with a limited set of use cases. You can do it using any design methodology. Where systems breakdown is when other viewpoints get added. For this system it might be the following: - Pricing/Invoicing - Prerequisites - Academic Status - Professor assignment - Course Reviews / Social Media All of these viewpoints are close enough to the registration system that there is a bias towards reusing as much existing code as possible. The problem is that at a high level two viewpoints look 95% similar, but in the code it equates to creating interdependencies between all of the viewpoints. Different teams might be successful in keeping the separation in the system, but in most systems I've seen the entanglement starts in the database with the entities. Columns that become nothing more than status flags for different viewpoints. Columns with near identical names that mean almost the same thing, but are handled differently because the viewpoints treat them differently. When the system gets big enough, a developer cannot mentally map the whole thing. When implementing a feature they will look for what is available vs what the architect had in mind. This is how you wind up with 10 different getCourse calls all of which are building off one another with various parameters. The code will have a lot of if/thens checking the parameters to make it work for a particular viewpoint and avoids bugs for the others. The more separation you have between the viewpoints the better. I now prefer separate entities/databases for every viewpoint. There is a set of entities common to all, but these are fact level entities. A course, A student, A professor, An admin, A TA. The entities should contain no status. Where the viewpoints need information from each other they should just query the appropriate viewpoint, or have the source viewpoint send out updates (great place for event sourcing). It might sound like I'm describing micro services. I wouldn't argue that, but I would say that a viewpoint is a higher level concept than a service. A viewpoint could be a collection of micro-services, or a single system (using this Clean Architecture).
- s73ver 9y agoI think a big part of that is this mistaken idea that, if you duplicate a single line of code, you've done a terrible, terrible thing. Sandi Metz did a pretty good talk about this, basically saying we need to think more before we abstract things away because "code duplication". https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstracti...
- unclebucknasty 9y ago>I've come around to the idea that the database is a better thing to put at the centre I've seen systems that take this to the extreme of not allowing a single piece of logic into the domain. Domain objects were essentially data containers only. So, if you had a Person object with firstName and lastName properties that represented those DB columns, then even a getFullName() that concatenated the two was verboten. Instead, all logic had to be in a service. This led to lots of duplication and a super-massive service layer in a system that was decidedly procedural, even if it was implemented in an OO-language.
- maxxxxx 9y agoI have also concluded that a lot of business processes can't easily be modeled by objects. There are almost always a bunch of exceptions that need to change change data directly .
- jl6 9y ago> So I've come around to the idea that the database is a better thing to put at the centre In my admittedly limited experience, applications come and go, but data lasts forever. So I find it unusual when a database is not at the heart of business software.
- arcbyte 9y agoData lives forever. Schemas live short lives.