1

Overview

I'm trying to learn about domain-driven design, and I'm about 80% of the way through Eric Evan's book on the subject (and about the same on the Pluralsight course). I've been attempting to apply what I've learnt in redesigning an old LabVIEW/TestStand project but I'm concerned about whether it's okay to have what I consider as the entities in it performing I/O logic, such as CAN/Modbus communication. The approach I'm taking now isn't very far from what I've done previously, and it's worked well for me so far so I'm fairly confident in it. However, I'd like to see if there's still room for improvement by viewing the development process from a DDD-perspective.

Beyond that, I'm hoping to just get validation on the approach I've taken in general. For that reason, this post will be a little long-winded - sorry in advance.

Background

This project is centered around performing automated tests of a certain brand of mechatronic (part of the transmission system of vehicles). I kicked off the project by collaborating with a colleague of mine who has less experience with object-oriented software development but a ton of expertise in automotive hardware testing. In this sense, he played the role of the domain expert. Using a whiteboard I modelled the test equipment and wrote down definitions of some of the terminology he used (these became part of the ubiquitous language):

As I started developing the project, I formalised this into a UML class diagram. The diagram below is the latest revision, slightly different from the first version due to an evolving understanding of the domain:

At the stage I'm at now, I have created most of the overall application architecture and implemented most of the logic. I've done the bulk of the code, leaving placeholders for my hardware-expert colleague to fill in with the actual hardware logic. We exchange back and forth using git, with my workstation being ideally suited for writing the bulk of the code and his for testing it against real hardware equipment (power supplies, motor drives, etc.). So far so good.

Typically, my approach to designing these applications is to create a LabVIEW "utility" library that models the test-rig and all the resources on it, and TestStand sequence files that make use of this library, performing a precedural sequence of steps like "Turn on power supply", "Set motor to 1000 RPM", "Measure pressures and check against limits", etc.

Looking at this from a DDD-perspective, it seems like the LabVIEW library constitutes the domain layer, while the TestStand sequence files constitutes the application layer, utilising and organising the classes in the library representing the domain to perform an actual test sequence that has value to our business. Furthermore, the VB test rig seems to serve as an aggregate root consisting of entities such as a power supply, a flow meter array, and so on.

Crux Of Problem

Here's where I'm a little unsure: Eric explains that entity classes should certainly contain behaviour appropriate to whatever it is they represent (to do otherwise would lead to an anemic domain model). Still, all the entity classes in his book (like Customer or Account) tend to have relatively "simple" behaviour - a Customer class does not need to perform I/O, for example.

Is it appropriate for me to still think of power supply and flow meter array as entities (when they're clear parts of the domain and ubiquitous language) despite the fact that I implement ZPL Power Supply with serial communication, and DAQ Flow Meter Array with data acquisition logic?

Additionally: have I suitably applied DDD principles, or is there room for improvement? Is DDD even appropriate to a project like this?

  • In general case, it's beneficial to keep actual I/O as far removed from the core logic of your code as possible, in a (thin) shell around your otherwise I/O-agnostic code. It helps testing and general clarity immensely, and also helps when production I/O requirements change. – 9000 Apr 30 '18 at 17:33
  • Can I ask why this was downvoted? – Tagc Apr 30 '18 at 20:16
  • I have no idea; I did not vote on this question. I can suspect that it's a bit overwhelming in size; it would benefit from boiling it down to the crux, with enough context around it. That is, the analytical work of going up from the crux and collecting only the relevant context is supposed to be done by the poster. High-level context is very important, though. – 9000 Apr 30 '18 at 20:23
  • @9000 Sorry for the confusion, I wasn't addressing you in particular. But in response to "I can suspect that it's a bit overwhelming in size", I did in fairness say right at the start "Beyond that, I'm hoping to just get validation on the approach I've taken in general. For that reason, this post will be a little long-winded - sorry in advance." – Tagc Apr 30 '18 at 20:26
3

I think DDD is not designed with this type of project in mind.

In my view DDD is mainly about connecting the language of the business with the underlying actuality of the code.

For example a sales person says: "I Sold 100 Widgets in Q1" a developer says: "there were 100 orders in the database with status complete and type "widget_v3" and completion date between 2001-01-01 and 2001-04-01 linked to employeeId 12"

This leads to confusion, DDD wants to get the developers thinking, and designing in the business language.

In your case it seems to me that the business language already matches the underlying technical reality. The things you are testing have technical definitions of what they do and everyone uses those terms to talk about them. In which case DDD is just OOP and its fine to put detailed code in methods (although better to inject it as a service)

  • "I think DDD is not designed with this type of project in mind." I think you're right. DDD doesn't seem well-suited to this project, but I still found many of the principles of DDD helpful (knowledge crunching, creating a ubiquitous language, identifying subdomains and reflecting them in module structure). – Tagc May 1 '18 at 7:37
2

Is it appropriate for me to still think of power supply and flow meter array as entities (when they're clear parts of the domain and ubiquitous language) despite the fact that I implement ZPL Power Supply with serial communication, and DAQ Flow Meter Array with data acquisition logic?

Probably not.

An important concept to understand in modeling is book of record. Is capital-T Truth in the model, or somewhere else (the real world).

My preferred illustration of the difference is shopping carts. When Amazon's model removes a item from my shopping cart, it's gone. But if I delete the box of cereal from the shopping assistant on my phone, the box doesn't magically leap back onto the shelf.

The Power Supply and Flow Meter Array sound to me like things outside your model that are the sources of information that your model receives. Likewise, you are probably sending messages to the Pump Motor Drive and other actuators, rather than including them in the model.

If you were writing a simulation of a Power Supply, then these things might be entities in your model.

In domain-driven design, are entities allowed to handle complex operations such as I/O?

Generally, no. You are more likely to see a modeled entity perform complex operations via a domain specific API (a "Domain Service"). A classic example here would be "send email" -- we don't usually include a bunch of email logic into the domain entities, but instead pass to them an interface that provides access to the email capability (typically implemented by an infrastructure layer).

  • "The Power Supply and Flow Meter Array sound to me like things outside your model that are the sources of information that your model receives." Then wouldn't all the classes I've listed in the UML diagram be "outside" the model? What would be considered inside the model? The implementations of these types under the Hardware folder all do some type of I/O. To clarify, these aren't regular classes but actors based on LabVIEW's actor framework, so they can each perform autonomous logic. – Tagc May 1 '18 at 7:31
  • "You are more likely to see a modeled entity perform complex operations via a domain specific API (a "Domain Service")." I considered this, but then literally my entire domain would consist of domain services, so I'd end up modelling a "power supply service", "flow meter array service", etc. in my domain instead of the more intuitive "power supply", "flow meter array", etc. I'm not sure but I think that @Ewan is right, in that DDD doesn't seem to fit this type of project. I appreciate this input, though. – Tagc May 1 '18 at 7:33

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.