Today, I took the first step in the direction of DDD: I created an application service that provides access to the domain logic. Every call to the domain logic ba the CLI app will go through this or a similar service.

August 13th

add application service

This commit adds an application service class in a new domain library. Pure DDD experts may not like this: strictly speaking, the application service layer should reside outside of the domain layer. For now, I’ll put application service and domain classes in the same library until there are enough classes to separate them - I’m no fan of having a separate library for every class.

The application service itself should not be much of a surprise: In the last diary entry, I wrote that the logic that will have to be implemented for the create command belongs in the core domain. Therefore, the application service gets a create method and pretty much the same test we had for creating an issue we’ve seen in the CLI app.

After all, I am in the process of factoring the existing functionality out of the CLI app, so the service needs to provide the same functionality for now. With that single test, the only functionality we test for is that a call to create returns the one string we test for. Don’t worry, I’ll get to actual logic in a minute.

You may have noticed the // NOLINT and similar comments: these are needed to avoid static analyzers like clang-tidy and cppcheck to complain about the create method not being static. It could be at the moment, but there will be a point where the method needs to store issues in a repository. This is a bit planning ahead, but I’d rather have those comments in the code for a few days than making the method static now and having to rewrite all call sites when it becomes non-static.

call application_service from cli app

Now that the application service provides the “create” functionality, it is time to call it. There is nothing much surprising happening: Instead of writing a hard-coded issue ID to the output, the CLI app now calls the application service to get the issue ID. The “Issue created:” part of the output is part of the user interface and stays in the CLI app. Since the create function does not rely on the input, empty strings are passed for now.

implement issue ID prefix algorithm

To satisfy the behave test, calling the “create” command with another title has to return the correct issue ID. Therefore the unit test is extended to check for that second call to create in the application service as well. The test could be satisfied by using if/else in the implementation of the function, but that approach would lead nowhere. We need the actual algorithm at some point, and TDD is about writing as little code as necessary, not as dumb code as we can get away with, even though the first lines might have looked like that.

For the implementation of the id prefix algorithm, I first wanted to reuse the split algorithm from the CLI app unit test. However, it does not quite fit the bill, so after copying it over there were some modifications and it looks much different now.

I think that ranges here show their strengths in terms of expressiveness. Ignoring corner cases, for now, the requirements say “an issue ID has a special format: four blocks of 2-3 alphanumeric characters each, and a block of 7 hexadecimal characters. Blocks are separated by hyphens. The issue ID is generated by abbreviating the first four words of the title…”. So, the algorithm for the prefix is:

  • split the title into words: title | rv::split(' ')
  • take the first four words: ` rv::take(4)`
  • take (at most) 3 characters from each word: ` word rv::take(3)`
  • put everything together, separated by hyphens: ` rv::join(‘-‘)`

As I wrote, corner cases like less than 4 words or words with less than 2 characters are ignored for now. There will have to be more tests to get that behavior right. There’s only one thing in the code that is not in the algorithm: the three characters in each word are transformed to lower case. Why is that?

It turns out that the behave test requires this behavior: the provided title has an upper case character, the required prefix is all lower case. The person who wrote the test (me) did implicitly assume that the ID prefix should be lower case. I asked the product owner (i.e. myself) to clarify that requirement, and he asked me (the developer) how much trouble it would be to implement the lower case version. “No trouble at all, half a line of code”, I said, so the product owner added a note about the lower case issue ID to the requirements document.

Compiling and running the tests brought a slight surprise: The application service tests were green, but suddenly the CLI app test was red: since the application service now evaluates the title, an empty string does not quite cut it anymore. The minimal effort to make the test pass again is to pass the required title to the application service. That’s sufficient to make the unit tests pass, but not the behave test: I’ll have to actually parse the title from the command line and pass it on to the application service in the next step.