Functional Application Services

Each business use case or key transaction in your application can be modelled as a unique, explicit application service. Modelled this way, and named as verbs, application services accentuate key business concepts — improving the alignment between your problem domain and codebase.

The traditional object-oriented approach to application services is now being outshone by functional approaches. Functional application services are more explicit, more concise and more maintainable — especially in functional languages like Scala.

In this post I’ll take you through an example of how I’m doing application services in Scala. The concepts are beginner-level functional programming, though they are definitely a stepping stone toward deeper functional patterns and concepts.

Note: A sample application to accompany this post is on my github. For a similar blog post demonstrating this pattern in C# I highly recommend checking out Mike Hadlow’s blog.

Application Services Recap

There are two really key benefits to application services. Firstly they represent actions that users can carry out, or internal business processes. For example: order me a coffee, reduce odds of sporting event, or select random winner.

A good application service expresses a business use-case by, importantly, coordinating domain objects and isolating them from technical bits and pieces like transactions or threads.

In Patterns, Principles and Practices of Domain-Driven Design we suggested naming application services as verbs, since we believe that verbs are often more significant in understanding a domain than the nouns (but certainly not always). An example from the book is the RecommendAFriend application service show below (and available on Github).

Note: We added the “service” suffix as a learning-aid. But I don’t recommend doing this in proper code.

This application service coordinates an online casino’s referral policy. If a user recommends one of their friends to sign up, the user will be upgraded to gold loyalty status and be allowed to blow even more of their weekly earnings on rigged games.

What is Wrong With This Code?

From a modelling and maintainability perspective, the RecommendAFriend application service is not a bad piece of code. It clearly expresses the use case it represents, it is not hard-coded to any implementations and it’s easy to understand exactly what it’s doing.

It’s the object-orientation and language (C#) that make this code noisy:

  • Class and method have the same name (because we model verbs not nouns) — duplication
  • Dependencies have to be passed in and bound to fields
  • each action requires {object}.{method} for invocation

Then there’s all the noise you don’t see in the code sample. Each dependency needs an interface and an implementation for testability. The tests then require mocks and stubs to be created and setup.

With functional programming and a language conducive to functional programming, we can make even this clean, SOLID OO code look embarrassingly OTT.

Functional Application Services

Moving to functional from object-oriented is a gradual process. An easy first step is to replace objects with functions. Applying that to the RecommendAFriendService, we can turn the whole thing into a function. Already we’ve removed the class and method name duplication:

From a Class to a Function

Using Scala’s exquisite apply() sugar, the RecommendAFriend application service can now be invoked using just it’s name: If you’re not familiar with Scala RecommendAFriend() is syntactic sugar for RecommendAFriend.apply() (apply is a special convention).

From a modelling perspective, this is super-expressive. However, the dependencies and the arguments have to be passed in to every invocation, which is undesirable noise. A simple solution, then, is to use multiple parameter lists (allowing partial application), with the first representing the constructor.

This application service can now be invoked with one parameter list to construct it, which will return the function (Int, NewAccount) => ResultType. Construction and invocation then looks like this:

From Object Dependencies to Function Dependencies

Now it’s time to remove the objects: NewAccountValidator, CustomerDirectory and ReferralPolicy. Each of these classes needs an interface and implementations. Noise which can be removed by using functions instead.

Additionally, these objects incur {object}.{method} invocation noise. Look at this snippet from the C# example:

Do the nouns add any value here? Or is it just important that we understand the key activity being carried out on each line?

Nearly all of this noise can be reduced if the application service depends purely on functions — no need for the interface boilerplate and elimination of the object name from each method call. Here is the functional alternative:

Wow! What on earth has gone wrong here? That is painfully ugly.

This code is ugly because the syntax for representing anonymous functions is ugly. In some languages you’re stuck with it. But in Scala, we have type aliases to make the problem go away.

Beautiful once more. We have the expressiveness of real type names whilst retaining all the benefits of functions (because they are still functions). In a language without type aliases I’m not sure what I would do.

Admittedly those type aliases are bringing some of the noise back that we got rid of when throwing away interfaces. But the difference is the type aliases are more concise than interfaces and pound-for-pound provide a net benefit in terms of expressiveness.

Contentious Abbreviations Time

Are you thinking what I’m thinking? Are you looking at that code and thinking the type names are exactly the same as the parameter names? Are you thinking that because we have named our functions, we have fully expressed their role using the type system, making the parameter names redundant?

I’m normally resistant to abbreviating parameter and variable names, except when the scope is short.

Shall we just have a look at what happens if we just use one or two digits for the parameter names? Shall we really? Do you dare me? OK, then.

What is this cryptic mess?

Note: This final functional version of the RecommendAFriend application service has a slightly more realistic asynchronous functional design than previous versions. Notably it uses Future, Either and returns events to be interpreted rather than directly carrying out the effects; essentially the domain events pattern. You can still use the domain events pattern in languages like C#. See the PPPDDD sample code for an example implementation.

Ok, now look again with an open mind. Let’s not rant about functional programmers and their incessant brevity. But let’s see if we discern what this small segment is trying to do:

Well v(n) is easy. v is defined 2 lines above, and n is defined 1 line above: ValidateNewAccount(NewAccountDetails).

Moving onto: map { err }. Again, looking a few lines up we can see v returns an Option[RecommendAFriendError] and just three lines down err takes a RecommendAFriendError and wraps it.

After crafting my application services in this manner for a while I am convinced that abbreviations improve the readability of the code, assuming that the scope of each variable is limited to just a few lines. And it’s absolutely essential that the type aliases are highly expressive.

I think these abbreviations make the code beautiful and concise whilst still being expressive. Of course, we have to expand the abbreviations in our head, but when everything is defined in a short scope that’s easy.

You may not agree, and I probably wouldn’t have until I’d tried it a few times.

Dependency Wiring

After years of constructing and threading complex object graphs together, believing that object-orientation was really the one true way, it’s almost unbelievable how easy it is to stitch together functional application services.

The pattern is low-ceremony. It almost feels like cheating. Invoking one function and passing other static functions into it. There’s no need for boilerplate interfaces and complex lifecycle management. It’s just functions… static functions being passed into other functions.

And There’s Still More to Come…

All I’ve shown here are a few baby steps. I think if you try this style of coding in a language with strong functional support, you will seriously start to question your beliefs in object-orientation. That’s not to say OO is not useful, but a proliferation of interfaces, implementations, stubs and mocks…. complex object lifecycles and dependency management — what the hell were we thinking!

After these baby steps comes more. Firstly there’s testing. So, so much easier since everything is just a function or data. You won’t know what to do with your new-found sanity. I think that will be the topic for my next post.

As you gain comfort using functions you start to yearn for higher-level functional concepts. You keep seeing patterns in the way you are stitching functions together and you start to wonder if there is a way to abstract over them… 

And that’s when you start to realise that Functors, Applicatives, Monads et. al. — they’re not academic waffle after all.