Tuesday, October 18, 2011

The value of using Domain Driven Design

From time to time I come across the relevant question of the value of using Domain Driven Design. Are there any hard numbers to use in convincing management about the benefits of DDD?

Hard numbers are really hard to get in software development since you never ever develop the same system twice and even if you did, it wouldn't be the same since you would have learnt a lot from the first attempt. But I'll share some thoughts and observations on using DDD.

By emphasizing communication with domain experts in developing the ubiquitous language DDD helps get you started on the right foot. Any system is simple from the beginning. The first set of functionality is never complex to implement since you have no other code to conform to and you tend to start with something basic. It means it is easy to just hack something together mixing levels of abstraction and business logic with technical complexity. Since first requirements are simple, the first complexity tends to come from unfamiliar tools like ORMs or other technical frameworks. Since we all are technicians at heart these things often become most important and the architecture tends to be more about technical concerns than the domain. All of which, we know, lead to a mess when requirements get more complicated.

By using DDD we are turning the focus away from technical concerns and instead focusing our effort on the business domain. With a bit of domain modelling, including test driven implementations, we get to investigate a larger set of requirements before going into technical details of persistence and other layers. A lean principle is to delay hard to change decisions to the last responsible moment, e.g. not deciding on a hard to change persistence model until we know what the domain really looks like. All in all, this leads to better architecture because the architecture is centered around the domain, not around technical frameworks.

Another aspect is complexity. What you consider complex depends on what you already know. I once worked with a large system where some parts were implemented using DDD and other parts were, what I would call, "undisciplined" transaction scripts. I think TS have their proper place and use, but implementing them in a way mixing business logic with technical concerns is never right. Hence "undisciplined". In this project I heard people complain about different things they thought was complex. Those people being unfamiliar with DDD but having a long history of working on the system thought the TS code with mixed concerns and very few abstractions was fine since they saw exactly what was going on and they "knew" what the context was and how everything worked on a detailed level. And they thought the DDD approach of separating things into different layers and building abstractions for business concepts just added complexity.

Then we had the newcomers, that didn't have any prior DDD-experience either, that struggled with understanding the domain. Even though DDD was new to them they pretty quickly picked up on the concepts and thought it was a great help in understanding the domain. To them it was the TS code that was most complex since it didn't make any difference between lines of business logic and those of database interaction.

These are two aspects on "the value of DDD". In another post I wrote about DDD being so much more than a set of technical building blocks and I think the most value from DDD is gained when wisely deciding on what parts to apply in each unique situation.

Wednesday, October 5, 2011

Is Domain Driven Design always applicable?

Disclamer: This blog post discusses use of DDD in information centric systems, which in my experience is most of the systems out there. It does probably not apply to technical software such as compilers and operating systems.

In my opinion the answer to the question in the title depends on how you define DDD. Do you do DDD if you do not use all technical building blocks? I think so. Is DDD more than the tachnical building blocks? I definately think so.

Personally, I would opt for using DDD in almost any situation but those that I know for sure will not evolve for more than a day or so and where I'm dead sure of the requirements. In any other situation I would start building the Ubiquitous language together with the domain experts to get a common vocabulary. This doesn't sound so much, but it is huge to avoiding expensive misunderstandings.

For the systems architecture I would always deploy the strategy to keep business logic and infrastructure code separated. It makes wonders for readability and testability. My rule of thumb is that business logic should be easily testable using unit tests (and end-to-end tests independent of the run-time environment) and infrastructure code should be so simple you generally need no other testing but a few basic integration tests.

For design my strategy is to keep the code close to the business in terminology, structure and logic. This is important to make sure that the impact of a change is about equal in size in both business and software. Here the ubiquitous language is an absolute need.

Then we come to the technical building blocks and now it is time for some choices. In general I think you could talk about two types of systems, or parts of systems; those mostly concerned with changes in object state and those mostly concerned with processing data streaming through the system.

In the first case entities, aggregates and repositories are a natural fit, in the second I think transaction scripts (in DDD context called domain services, since they do only concern domain logic, no infrastructure code, as per discussed above) are a nice fit. When the most important feature is to crunch some data, perhaps modify it and then route it further to some recepient (like another system or some persistent store) I think it is the "processing pipeline", i.e the stateless service code, that should be emphazised. So in those cases the internals of the data isn't very interesting and might be better left in some simple DTO format.

Whether going with entities or servicies I think the practice of using typed value objects for input parameters give a lot in terms of clarity and IDE-support when writing and reading the code. From a technical perspective, this is the easiest DDD building block to use and I think it has great advantages even if it is the only part of DDD that you apply to the system.

In conclusion, for the types of information systems I've been developing over the years DDD is the way to do it, however that doesn't necessarily mean everything should be entities or value objects. DDD is so much more.

Sunday, August 28, 2011

Unit vs Integration tests

Lately I've spent some time thinking and discussing pros and cons of unit and integration tests. Basically I think you need both but they have totally different characteristics and should therefore be combined carefully. Before we dive in, lets define the terms for the sake of this discussion since I know there are at least as many definitions of "integration test" as there are projects out there.
Unit test: Test focused on one or a closely related set of classes. Defined and run outside any run-time environment (container). All dependencies are mocked/stubbed.
Integration test: Test focused on one or several (sub)systems. Defined in code and run in the run-time environment. Some, but not all, dependencies might be stubbed.
With definitions done, let's go for some characteristics I've found to be true.
Unit test - pros:
1: Small units (single classes or a small set) are tested independently without interferences from the surrounding environment.
2: Can be run instantly with a few keystrokes - no deployment necessary, the whole (sub-) system doesn't even have to compile. Makes it easy to test incrementally.
3: Supports Test Driven Development.
4: With small units it is easier to cover all interesting execution paths.
Unit test - cons:
1: Mocking frameworks could be a challenge to master, but they are oh-so useful when you get to know them.
2: Different tests must be in sync to ensure that individual units work together. Can be tricky, especially when dealing with semantic changes in the interface of a class/unit. Requires disciplined top-down TDD.
A colleague of mine pointed out that my con #1 actually could be a pro if using TDD (as in writing test and production code simultanouosly) since having a hard time mocking dependencies would be an incentive towards writing code with fewer dependencies, which in general is a good design practice. However, my experience is that a good mocking framework is necessary to effectively write unit tests, and as with every tool there is learning curve. Now for integration tests.
Integration test - pros:
1: Tests the code in its true context with few or no stubbed dependencies.
2: Supports testing of non-functional requirements, such as performance, security and transaction boundaries.
Integration test - cons:
1: Can only be run after the (sub-) system has been fully developed, built and deployed. Tests are written as an afterthought instead of driving the development.
2: The code-build-deploy-test-cycle takes a long time which make debugging cumbersome.
3: Might require other parts of the system than those under test to be fully functional in order to bring the system into the state for the test to start. This creates strong dependencies on parts of the system that are not targeted by the test.
Most of my cons for integration tests only become severe when integration tests are used to test business logic, which I've seen from programmers claiming unit tests with mocked dependencies being too hard to write. When used to test real integration issues (such as inter-system communication or database connectivity), the cons sort of goes away since those issues cannot be detected anyway prior to having at least a "walking skeleton" implementation of the architecture.
In conclusion, I think both unit and integration tests (as well as automated systems/acceptance tests) are really needed in a software project. However, each need to be used propely. For ensuring implementation of business logic I think unit tests are the proper choice. It might be a bit harder to write because you would have to mock dependencies instead of relying on the state currently set up in the database, but at the time of adding functionality or refactoring your domain code unit tests are so much easier and thereby cheaper to run since you do not need to bring your complete system into a known state, you do not even have to compile, let alone install, your complete system. Striving for a good coverage (some say a 100%, I don't) of your domain logic, preferably using a test-first approach, you also tend to achieve a better design since well designed objects are easier to test.
Side note: Today we got lucky like a crazy when we found a bug in the business logic because we by chance happened to run the integration test designed to test that feature. It could be, and was, claimed to be an argument for the benefit of using integration tests for testing business logic. However, I think it is not. For starters, the bug had been in the code for about five months. Not even the guy writing the feature knew were in the system the code was located. We had to spend several hours to find the implementation. After that, the bug was easy to spot. It gives a perspective on cycle time and why quick feedback is important. Secondly, the bug could not be found by unit test since there was no coverage on this part of the system. I'm pretty sure the reason for that is that it is really hard to write unit tests for code directly manipulating a JPA entity manager. So, the problem was really that business logic was burried in the database access code and hence not unit tested. If a unit test had been attempted I'm pretty sure the logic had been moved into a more test friendly design. Which of course would have been better from other perspectives as well.