In the last couple of weeks I've been occupying myself with porting the Blackjack Java project to Clojure. I've been curious whether DDD concepts could be reused in a functional language without sacrificing idiomaticity. There is still a long way to go, but the walking (limping) skeleton is ready. Check it on github. In the followings I make an attempt to summarize my experiences.
1. Aggregates
First of all Clojure strongly encourages working with immutable data structures. My naive assumption had been that an OO aggregate can be simply substituted with a data structure accompanied by a handful of functions operating on it. This is only partially true. Though I've found state mutation easily replaceable with creating a new data structure (Clojure's persistent data structures make it performant, and its very effective data manipulation functions easy) as the output of the applied function, but I'm not convinced about how invariants should be preserved in the absence of an entity with mutable state. To some degree it can be achieved by :pre and :post constraints on the individual functions, but I haven't really dived into it.
2. Domain events
Related to the previous point, since in OO aggregate roots are to publish domain events. Initially I implemented my "aggregates" the same way, the functions operating on them published events as a side-effect. But eventually I've realized it's very non-FP, so instead of invoking a side-effect, I changed these functions to return not only the updated data structure (the new state of the "aggregate"), but the event-list as well. Then the new "state" is updated in the DB and the events are sent out. This solution (which could be applied in OO as well) eliminates the need of thread-local/event store-flushing magic I resorted to in the java version. I am more than satisfied with this, it's not only idiomatic, but makes things so much simpler.
3. AOP for locking
This is quite pleasing also. Instead of aspects and annotations, it's a 5-(short)-line macro.
4. Layering
The most interesting part. In the very few books mentioning the large-scale structure of FP-programs I've stumbled across the notion of onion-layering, not unlike that of DDD or Hexagonal Architecture. We should strive to confine the mutable states to the outer layers, keeping the core completely functional. Unfortunately in OO-DDD, the "core" is the Domain, which does have logic with side-effects. Repository interfaces and Domain Services as front-doors to ACLs are very much part of the Domain, and are there to enable the Domain to interact with the external world (writing to DB, sending messages, calling web services, ...).
So quite early it became clear that the layering in FP-programs requires some changes in the OO-way of thinking.
4.1 Hexagonal Architecture
If we forget about DDD temporarily and reach back to HA (which I think is one of the foundations of the former), some parallels with the FP-version of onion-layering are obvious. In HA, the Adapters are outside of the core and the Ports are the joints that bind the Adapters to the core. Even an FP-program must interact with the external world (otherwise what's the point of writing it), so HA, with its explicitly designated areas of external interaction seems to be a natural fit.
4.2 Application layer
Having a clean facade for an application has nothing to do with OO or FP, so keeping this layer of DDD seemed ok. In addition to the normal DDD application layer responsibilities, such as locks, transactions and orchestration, I decided all calls to secondary (driving) ports should happen here (calls to a remote Bounded Context, Blackjack Wallet, interacting with the DB). This is the external layer of the onion (still beneath the adapters-layer, though).
I've kept different namespaces in this layer for the different modules (Player, Table, Game) and used most method names for commands with bangs.
4.3 Core (Domain) layer
A purely functional layer. I've tried to simply port the java version's Domain here, minus the secondary (driving ports), e.g. the Game-, Player-, TableRepository or the WalletService, which I've decided to put under a port package on the same level as app and domain. Secondary ports neither defined (but in their own package) or used (but in app services, see previous point) here.
4.4. Infrastructure layer
Quite the same as in the java version. Adapters are implemented as Records of Protocols, see next point.
5. Using Protocols for secondary ports
I've tried to avoid OO-techniques as much as possible, but protocols simply seem to be the right way to implement ports.
6. Application Context and Dependency Injection
Although in the absence of objects FP doesn't require DI the same way as an OO program would, I've found the use of Protocols has similar needs to that of objects. The Protocol implementation has to be accessible to the function that uses it without explicit dependency on it. In a Java-Spring application the application context would do this wiring, as well as instantiating the proper implementation based on the content of a property file. Lacking any framework I created a dedicated namespace for it, called registry. All namespaces where functions need to call Protocols import the registry namespace. I don't know how others do this, but this solution seemed straightforward.
7. Modules
I kept the module structure of the Java version intact. Game, Table, Player and Cashier are the same, they interact with each other in an asynchronous way. Clojure Async does this job perfectly. There is a channel for Domain Events and subscriptions for the event handlers. Much shorter and cleaner than the Java version's was.
To be continued...
Friday, 11 July 2014
Friday, 16 May 2014
Clojure practice - 2
After finishing the previous post I noticed that Kaloz's solution was a bit more concise, not leaning on a helper function. So here is a "more compact" version, even puttin the number->characters mapping definition in the same expression.
or, purely for the sake of example, having even the println in it
(defn compact-combinations [number]
(let [num->chars { \2 "ABC" \3 "DEF" \4 "GHI" \5 "JKL"
\6 "MNO" \7 "PQRS" \8 "TUV" \9 "WXYZ"}
red-fn (fn [acc d]
(m/match (num->chars d)
nil acc
chs (for [c chs
i acc]
(str i c))))]
(reduce red-fn [""] number)))
(println (compact-combinations "34"))
or, purely for the sake of example, having even the println in it
(let [combinations (fn [number]
(let [num->chars {\2 "ABC" \3 "DEF" \4 "GHI" \5 "JKL"
\6 "MNO" \7 "PQRS" \8 "TUV" \9 "WXYZ"}
red-fn (fn [acc d]
(m/match (num->chars d)
nil acc
chs (for [c chs
i acc]
(str i c))))]
(reduce red-fn [""] number)))]
(println (combinations "514")))
Thursday, 15 May 2014
Clojure practice
Recently I have dabbled into Clojure. I've been planning a post for a while, but since now Kaloz has posted a Scala functional programming demonstration, the time has come to reply in kind. Please, read his post to get the problem, here I take the lazy way and simply show a Clojure solution.
(ns clojure-study.garbage
(:require [clojure.core.match :as m]))
(def num->chars {
\2 "ABC"
\3 "DEF"
\4 "GHI"
\5 "JKL"
\6 "MNO"
\7 "PQRS"
\8 "TUV"
\9 "WXYZ"})
(defn- red-fn [resolvedStrings number]
(m/match (num->chars number)
nil resolvedStrings
item (for [c item
i resolvedStrings]
(str i c))))
(defn combinations [number]
(reduce red-fn [""] number))
(println (combinations "77501998489"))
Thursday, 1 May 2014
DDD reference project
I haven't posted in a while, but wasn't completely idle. I'd like to improve our way of doing DDD in the team I'm working in, so I have endeavoured to create a reference DDD project, where good practices, distilled experiences and some new ideas are demonstrated. The result is here, general explanatory notes on the wiki. It was quite a task, but a rewarding one.
Wednesday, 26 February 2014
Defining your own exception types
I'm in a habit of creating different classes for different exceptions, as opposed to for example using RuntimeException-s or IllegalStateException-s describing details as a string in the constructor. I'm not hundred percent sure that's an ultimately better practice and recently I've watched a talk where the speaker adviced against class proliferation. True, in a language like Java, creating classes is very boilerplate. In Scala an exception class is probably half a line of code, and you can stuff a dozen in the same source file.
But, yesterday I realized there is a situation where a custom exception class comes handy. Imagine you put some validation logic in your domain class.
The Validate can be a validator class from Apache Commons or some custom class you wrote throwing IllegalArgumentException-s. At the project I'm working on right now we are using messaging and ActiveMQ as the integration solution. If there is an exception, the message is bounced back to the queue and the broker retries to deliver it a couple of times before it gives up and puts the message in the Dead Letter Queue. So if the message had a null as a player id, no matter how many times the broker retries it will always fail, but in the meantime the process consumes time and threads, possibly generating the same error log for a number of times. Instead of this if you create a( possibly abstract) n custom exception for domain problems, like
And at the entry point of your system you do a try-catch for it
Then you can catch and handle the problems where there is no chance of recovery in retrying.
But, yesterday I realized there is a situation where a custom exception class comes handy. Imagine you put some validation logic in your domain class.
public class PlayerId {
private final String id;
public PlayerId(String id) {
Validate.notNull(id);
this.id = id;
}
}
The Validate can be a validator class from Apache Commons or some custom class you wrote throwing IllegalArgumentException-s. At the project I'm working on right now we are using messaging and ActiveMQ as the integration solution. If there is an exception, the message is bounced back to the queue and the broker retries to deliver it a couple of times before it gives up and puts the message in the Dead Letter Queue. So if the message had a null as a player id, no matter how many times the broker retries it will always fail, but in the meantime the process consumes time and threads, possibly generating the same error log for a number of times. Instead of this if you create a( possibly abstract) n custom exception for domain problems, like
public abstract class DomainException extends RuntimeException {
public DomainException(String msg) {
super(msg);
}
}
And at the entry point of your system you do a try-catch for it
try {
// do the real thing
} catch(DomainException ex) {
//do something, e.g. logging
}
Then you can catch and handle the problems where there is no chance of recovery in retrying.
Labels:
exceptions
,
OO
Tuesday, 25 February 2014
JPrinciple
Finally, I've finished porting the code to Scala + made a wikipage on Github. By the way, Github is awesome.
So the source code: https://github.com/matemagyari/principle
And the Wiki: https://github.com/matemagyari/principle/wiki/JPrinciple
Labels:
code analysis
,
DDD
,
JPrinciple
Tuesday, 18 February 2014
DDD - Modules, Subdomains, Bounded Contexts
The exact meanings of these concepts have always eluded me, until yesterday I read some chapters from Implementing Domain-Driven Design, which has shed some light on the topic. I'd like to share some thoughts I think I've learnt.
Subdomains
Before going to discuss subdomains, let's talk about what Domain is in general. Domain is not something we build, but something that actually exists in the world. We only try to create a Model for it. Subdomains are also something existing independently of how they are modelled (if modelled at all). If we take Amazon as an example, users can browse the purchasable items, search for specific ones, have recommendations from Amazon, etc. This is a subdomain of the whole Amazon domain, we can call it Item Catalog subdomain for now. The user can also order items, view her previous order history, and manage her orders in some ways. This is the Order subdomain. These subdomains exist regardless of how Amazon implemented its application. Even if it's one monolithic ball of mud, using a single database table for everything, these subdomains are there. One purpose of DDD is to make them explicit and visible by a code base that reflects the structure of the Domain.
Bounded Contexts
As opposed to Subdomains, BCs are defined by the developers. A BC is where a ubiquitous language applies. Classes with the same name can mean different things in different BCs in the same domain. For example User in Item Catalog is someone with a browsing history, search patterns, recommendations. In the Order subdomain User is someone associated with orders and the ordering process. They are two different aspects of the same human being. But there is a one-to-one mapping between the two, probably manifesting in the same UserId.
Ideally, there is a one-to-one mapping between a Bounded Context and a Subdomain, so once the developers identified a subdomain, they design its own Bounded Context. Unfortunately in real world applications, where the boundaries of subdomains are not well recognised, BCs and Subdomains often overlap, a BC containing one or more Subdomains, or a single Subdomain is modelled with multiple BCs, or (in the messiest case) the overlap can be only partial.
I think a BC should be one deployable artifact (e.g. a jar or war file), but Vaughn mentions BCs implemented by multiple deployables, and I'm not sure whether packing multiple BCs in one component is a bad or good idea.
Modules
Modules in Java are simply packages. A Module can contain multiple aggregates if they are cohesive enough, or just one. Modules are really a less coarse-grained, lightweight versions of Bounded Contexts.
Subdomains
Before going to discuss subdomains, let's talk about what Domain is in general. Domain is not something we build, but something that actually exists in the world. We only try to create a Model for it. Subdomains are also something existing independently of how they are modelled (if modelled at all). If we take Amazon as an example, users can browse the purchasable items, search for specific ones, have recommendations from Amazon, etc. This is a subdomain of the whole Amazon domain, we can call it Item Catalog subdomain for now. The user can also order items, view her previous order history, and manage her orders in some ways. This is the Order subdomain. These subdomains exist regardless of how Amazon implemented its application. Even if it's one monolithic ball of mud, using a single database table for everything, these subdomains are there. One purpose of DDD is to make them explicit and visible by a code base that reflects the structure of the Domain.
Bounded Contexts
As opposed to Subdomains, BCs are defined by the developers. A BC is where a ubiquitous language applies. Classes with the same name can mean different things in different BCs in the same domain. For example User in Item Catalog is someone with a browsing history, search patterns, recommendations. In the Order subdomain User is someone associated with orders and the ordering process. They are two different aspects of the same human being. But there is a one-to-one mapping between the two, probably manifesting in the same UserId.
Ideally, there is a one-to-one mapping between a Bounded Context and a Subdomain, so once the developers identified a subdomain, they design its own Bounded Context. Unfortunately in real world applications, where the boundaries of subdomains are not well recognised, BCs and Subdomains often overlap, a BC containing one or more Subdomains, or a single Subdomain is modelled with multiple BCs, or (in the messiest case) the overlap can be only partial.
I think a BC should be one deployable artifact (e.g. a jar or war file), but Vaughn mentions BCs implemented by multiple deployables, and I'm not sure whether packing multiple BCs in one component is a bad or good idea.
Modules
Modules in Java are simply packages. A Module can contain multiple aggregates if they are cohesive enough, or just one. Modules are really a less coarse-grained, lightweight versions of Bounded Contexts.
Subscribe to:
Posts
(
Atom
)