2

I'm having trouble modeling the Business Objects of my application.
In my domain I have substantially a list of bills of orders, and for every bill I have the pallet that contains the materials to satisfy the orders. A bill is a complex object, it contains several properties, and a list of orders, which in turn are complex objects. The pallet object is likewise complex, it contains several properties, a list of stacks and a list of distributions of the contained stacks; this lists too contain complex objects.
The software is structured as 3-layers application (data access layer, business logic layer and presentation layer); Presentation layer follows MVVM pattern, and in DAL layer I'm using a Repository class for every data model entity.
enter image description here
My problem is how to define Business Objects. For example, in one view I have to show the list of all the bills with the details, specifying the status of the associated pallet, so my classes may be:
enter image description here
Instead, on another view I have to show the data of the pallet under processing, and some data of the associated bill, so in the classes the direction of the relation between Bill and Pallet should be opposite:
enter image description here
So, how should I design my classes? These are some my observations:

  • I could model my domain with different classes depending on the use case, but I don't think is correct. In my opinion for a domain only a single set of business classes should exist, so I have to build classes that I can use for all the use cases
  • I could establish a bidirectional relation between Bill and Pallet, but I think is better to avoid it. Moreover, this solution would force me to implement some kind of Lazy Load, increasing the complexity
  • I could remove the relation between Bill and Pallet, so let the classes less coupled to use cases, and create classes on the view models (so in Presentation layers) for the specific use cases. The problem in this case is that I should move some business rules on Presentation layer.
    enter image description here
  • I could remove the relation between Bill and Pallet, so let the classes less coupled to use cases, and create classes on BLL for the specific use cases. The problem in this case is that in my BLL I would have very specific classes, so it would become a less generic layer.
4
  • 1
    "The problem in this case is that in my BLL I would have very specific classes, so it would become a less generic layer" - BLL is not supposed to be a very generic layer, and in fact use case–specific classes should form an outer "shell" of the BLL. This layer captures the business logic specific to the problem domain, and it's only generic to the extent that it supports two or more related applications dealing with roughly the same domain, but that comes later on when the system is more mature (if at all). 1/2 Aug 25, 2020 at 12:12
  • BTW, those use case–specific classes would have methods that would return something like your BillWithPallet class, so you'd define that in the BLL as well, but consume it from your Presentation layer. That said, once you have that outer shell, depending on how you conceptualize it (e.g., if your classes return something like BillWithPallet or BillWith[SubsetOfPalletProps]), it may no longer be necessary to remove the Bill-Pallet relationship, and then you can go with the direction that makes more sense according to your understanding of those business concepts. 2/2 Aug 25, 2020 at 12:12
  • For the bidirectional relation, you don't need lazy loading. You just need some caching so that when the user asks for Bill#42 and the associated pallet does the same for establishing the relation, that both get the exact same object. And the cache should already provide that object while its relations are being built. Aug 25, 2020 at 13:02
  • Run away from "generics". Some ppl is too obsessed with making everything generic or abstract. Concreteness is not the Evil! Let abstractions emerge!
    – Laiv
    Aug 26, 2020 at 11:49

1 Answer 1

-1

How to model domain classes for the business layer is a topic on which full books were written. It would be far too broad to answer in a general way, but here a couple of remarks that should help you to avoid some common traps:

  • First, you need to separate concerns: the domain is not the application. The application provides functionality to its users to work with the domain. Sometimes an application covers completely the domain, but sometimes the domain is shared between several applications. Moreover if you decompose your app into distinct layers, it’s exactly to separate different concerns and to avoid business functions from being so deeply intertwined with other concerns (presentation, persistence, ...) that it’s not maintainable in the long run. For all these reasons, the domain deserves to be well understood.

  • Requirements may help you to identify the domain objects, regardless if you use use-cases or user-stories. Use cases are typically used to identify domain entities in a systematic way, using the Entity-Control-Boundary approach. In this regards, you have already identified a lot.

  • You need to design the domain according to the relationships in the domain, independently of the current application will present these. A wise pioneer once said: "Premature optimisation is the root of all evil"

  • Requirements are always a good opportunity to liaise with the users for challenge your reality in addition to clarifying their expecations. Example: Is there really 1 bill for 1 pallet? Always? Maybe it's ok and then go on. But if your're in doubt, clarify it first to avoid painful surprises. Typical counter-example: there's a first invoice, but due to a price increase or a customer discount granted after a complaint, there's a second invoice after all. Or worse, somle items on the palette were returned and credited to the customer.

  • Domain Driven Design can really help you to use the right approach here. Get Ewan's book and you'll see a lot clearer how to struture the relationships according to domain needs.

  • Domain models show associations, but rarely limit their direction because it's not a core concern for the domain. As you already found out: one view may need one direction, and another view the reverse. And maybe in a couple of month, you'll have the need to start navigation at a pallet stack? Your design shall remain evolutive. If you are in business applications, the performance gains that you could hope by implementing only mono-directional associations are tiny compared to the cost to later implement the inverted navigation. And if you use DDD, anyway, the repository design will make this kind of worrying useless: you need a pallet? Ask the PalletReportory by id; you need a bill? Ask the BillRepository by id. You need the pallets based on a bill? Ask the PalletRepository by bill_id.

9
  • I disagree with every one of your points here, the most with your first point: "you need to separate concerns: the domain is not the application...The application just provides functionality...". "Just"? The application's main purpose is to provide that functionality. This can not be an afterthought. There can be no model independent of how it's used. Separating the concern to provide useful functionality from its data is about the worst thing one can do. Aug 26, 2020 at 11:01
  • @RobertBräutigam Let’s clarify: the application adresses several concerns, such as presenting, business logic, and persistence. Decomposing these in different layers (or cores, depending on the architectural layer) makes only sense with the right focus. Moreover (at least in my area) same domains are often shared by several applications, each specialized on some tasks. The issue I see everyday is that sometimes people build specialized domain that inappropriately represents what it’s supposed to represent, and that are difficult to upgrade because completely coupled with application logic.
    – Christophe
    Aug 26, 2020 at 12:05
  • @RobertBräutigam This being said, it’s your right to disagree and DV, and I appreciate the opportunity for clarifications, but it would be more constructive to provide your own answer so that we can see if there is at least some common ground (or if just must disagree as strongly as you) ;-)
    – Christophe
    Aug 26, 2020 at 12:13
  • I only know the usual "business applications" (banks, retail, utilities, etc), so there might be areas I haven't experienced where the above is needed. However, even in DDD which you cite, "bounded contexts" are introduced exactly for the reason not to share "domains". Also, as mentioned, I don't want to address "presenting, logic, persistence" separately, because they are closely related and coupled, i.e mostly change together. The Layered Architecture makes applications less maintainable. Aug 26, 2020 at 12:32
  • @RobertBräutigam Bounded context are not meant to keep domains private to applications, but to separate distinct domains. If you have a warehouse domain, some applications may deal with inventories, other with picking items, still other with optimizing physical movements, some for controlling warehouse robots. It’s all about pallets, materials, storage locations and incoming and outgoing deliveries. If you decide up-front to have 1 domain per application, you have decided up-front on the architecture, not necessarily the leanest one.
    – Christophe
    Aug 26, 2020 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.