Showing posts with label DIP. Show all posts
Showing posts with label DIP. Show all posts

Saturday, 2 November 2013

DDD and Hexagonal architecture

Since two years ago I had the pleasure the start using DDD at my workplace I can hardly imagine developing software without it (long-haunting experiences from previous projects might have something to do with this). There is a lot to love here: Entities, Value Objects, Repository pattern, Aggregates, Bounded Contexts, Ubiquitous Language, Anti-corruption layers, ... But for me the most valuable part of it is the idea of placing the Domain in the heart of the application and building everything around it as opposed to the traditional layering theory, where the UI is on the top, the Domain in the middle, and everything lies on the DB at the bottom.

----------------------------------
UI Layer
----------------------------------
Business Logic Layer
----------------------------------
Persistence Layer
----------------------------------

This idea, placing the database and the data model at the center, have proved to be very harmful, often resulting in anemic objects and procedural code. And goes against one of the most fundamental concept in OO design, the Dependency Inversion Principle. High level modules should not depend on low level modules. What's so special about databases anyway? What if our application instead of using a DB directly has to cooperate with a legacy system, storing the data through it and communication is based on web-service calls? And before it "stores" the data it communicates with other components through web-services? What I'd like to point out, that if we follow the "traditional" layering model, we have to assign the first WS calls to the Persistence Layer, but the second to the BLL. The distinction is simply arbitrary and contrived.
What DDD does, using an onion-like layering structure instead of the vertical, one-dimensional one, is what Alistair Cockburn proposed as Hexagonal Architecture, or Ports and Adapters Architecture, even before DDD appeared on the scene.



Please follow the link before read further. This could completely change the way how you think about software. Funny enough in spite of being around for almost 10 years by now I have yet to see a nice example on the net. So I'd like to fill this gap now.

Hexagonal (Ports and Adapters) Architecture example

Instead of the usual and boring Pet Clinic or Ordering application I choose to use an unlikely, but at least more interesting theme. Let's build an application that receives captured secret messages from the enemy, asks an other component to break them, then stores the decrypted messages. Here are the classes
//infrastructure layer
class MessageListener {
     void handleMessage(String jsonMessage) {
         EncryptedMessage encryptedMessage = getEncryptedMessageBuilder().build(jsonMessage);
         getCodeBreakerAppService().breakAndStore(encryptedMessage ); 
     }
}
class WSBasedDecrypter implements Decrypter {
   // call a WS to do the work
}
class MongoDecryptedMessageRepository implements DecryptedMessageRepository {
   // store stuff in Mongo
}
//app layer
class CodeBreakerAppService {
   void breakAndStore(EncryptedMessage encryptedMessage) {
        authentiationCheck();
        startTransaction();
        getCodeBreakerAndArchiver().breakAndArchive(encryptedMessage); 
        endTransaction();
   }
}
//domain layer
class CodeBreakerAndArchiver {
   private Decrypter decrypter ;
   private DecryptedMessageRepository decryptedMessageRepository ;
   void breakAndArchive(EncryptedMessage encryptedMessage) {
        DecryptedMessage decryptedMessage = decrypter.break(encryptedMessage);
        decryptedMessageRepository.archive(decryptedMessage); 
   }
}
interface Decrypter {
    DecryptedMessage break(EncryptedMessage encryptedMessage);
}
interface DecryptedMessageRepository {
    void archive(DecryptedMessage decryptedMessage);
}

Imagine the app is listening to a message broker, like ActiveMQ. We have a MessageListener instance, which is configured to listen to a JMS queue. For the sake of simplicity, imagine that the messages coming in are simple JSON strings. So, assuming you've read the article, you can surely indentify the ports and adapters of this app. As a reminder, a port is where our application interacts with the external word, sitting just on the boundaries of the application. We have 3 in our app.

1. The breakAndStore public method on CodeBreakerAppService. This is a driven port, which is called (indirectly) by some external entity. The entry point of our app. The adapter, that transforms the message to something the port can understand is the MessageListener. I said indirectly, because the message have to go through some integration layer (JMS listener mechanism here), then the adapter which eventually passes it to the port.

2. The Decrypter interface. This is a driving port that is called by the domain, triggering some effect on the external world. It is implemented by an adapter, in this case WSBasedDecrypter.

3. The DecryptedMessageRepository interface, similar driving port implemented by the MongoDecryptedMessageRepository as the adapter.

Now let's imagine another team in our company wants to use our app to spy their own enemies, but they abhor NoSQL (shame on them) and want to store the encrypted messages in Oracle. No problem at all. We only have to create a new implementation of the DecryptedMessageRepository interface, let's say JDBCDecryptedMessageRepository, and can plug it in the Domain in runtime if needed. Or if you don't want to store the messages but spit it out on-the-fly to the screen, you can create another implementation sending messages to the UI (although Repository may not be the most appropriate name for it anyomore). The point is, there is no up (UI) and down (DB) here. Just an outer layer of onion wrapping around the core. The adapters are responsible for the details.
Then the Product Owner says we need to open the app not only for JMS, but REST as well. So let's configure MessageListener as a REST endpoint too, or create a new class for that. Our Domain (and application layer) is intact. The Domain independent of all these details, not a single line needs to change.

This architecture style is so simple and elegant, I can't understand why Hexagonal Architecture hasn't become a household name by now. And it hasn't. Most often if I mention it to other developers I meet blank faces. Sometimes it rings a bell, I've failed to meet anyone saying, "yeah it's cool and we use it all the time".

Another interesting thing is that, I think, if you follow DIP, you can't help ending up with this. It just grows out of a very simple principle. That's it for now.

Thursday, 31 October 2013

Searching for packaging guidelines - 1

Defining a satisfying package structure always gives me a headache. Sometimes I feel it "right" but until recently I've had no formal principle to rely on to confirm my hunch. Couple of weeks ago I started developing a plugin that can calculate design quality metrics and so I've dug into topic, and I think I found some useful stuff.

Let's see the problem in an simple enough example, which works with very few components to make it concise but enough to demonstrate the point. So we have the following classes
interface Compressor {
  CompressedContent compress(UncompressedContent uncompressedContent);
}
interface CompressedContent { ... } 
class UncompressedContent { ... } 
class ContentDownloader {
  void downloadCompressAndSave(Compressor compressor) {
       UncompressedContent uncompressedContent = downloadContent();
       CompressedContent compressedContent = compressor.compress(uncompressedContent);
       save(compressedContent);
  }
}
class ZipCompressor implements Compressor { ... }
class RARCompressor implements Compressor { ... }

Our mini application downloads contents from God knows where, compresses and saves them. Different compressing algorithms can be plugged-in easily (Strategy pattern). The ContentDownloader uses other components too, but they are not relevant for our example, so let's just say ContentDownloader from now represents a bunch of classes. Similarly there can be several other implementations of Compressor. The question is, how would you package your components?
In the following, lacking both a UML visualisation plugin and the commitment to get one I will use a simple notation. Dependencies between packages will be represented by '-->' and packages by ( <class1>, <class2>, ...). So the structure where the package containing classA and classB depending on package containing classC and classD, and both are in the same superpackage will be represented by

( (classA, classb) --> (classC, classD) )

Solution 0

(ContentDownloaderCompressor, ZipCompressor, RarCompressor)

Everything in one bag. Obviously it's not a good solution. The packaging should one way or another represent the structure of the application and tell the developer something about it.

Solution 1

 (ContentDownloader) -> (CompressorZipCompressor, RarCompressor)

Seems better. We've confined all the compressor code (interfaces and implementations) into one package. However I like having classes belonging to the same abstraction level in a package. Having interfaces and their implementations in the same place, I feel, violates this.

Solution 2

( ContentDownloader -> (Compressor <- (ZipCompressor, RarCompressor) ) )

This is something I see quite frequently. For example put the façade interface under the package org.something.app and the implementation under org.something.app.impl.

Solution 3 

(ContentDownloader) -> ( (Compressor) <- (ZipCompressor, RarCompressor) )

A variation when the interface is under org.something.app.client and the implementation under org.something.app.impl.

The common problem with both last approaches is that (ContentDownloader) depends on a package both containing the interfaces it uses, and the implementations it has no knowledge of. Why is it a problem? Let's stop here for a short intermezzo and ponder what we want from packages actually. What I want, for one, is that if I need to do some change in functionality, I have to touch as few packages as possible and in the packages I do have to, I want as few classes as possible not related to the change (This is called the Common Closure Principle).Why? Because unrelated components localised closely to the place of change are noise.
Or to view it from another perspective, let's a run a small hypothetical experiment. Let's assume the packages are units of release (even if not, the general idea is worth considering). Adding another implementation will require recompiling the full package ( (Compressor) <- (ZipCompressor, RarCompressor) ) and (ContentDownloader)  too. This is bad, since no code change has happened in (ContentDownloader). And it wouldn't be enough to release ( (Compressor) <- (ZipCompressor, RarCompressor) ), we would have to release the bigger package containing everything. Experiment ends.
Again it's only hypothetical, because in Java the packages are not units of release. But I like to find general principles behind things on different scale, and I think structuring your deployable components (projects) or libraries has lot in common with package structuring. After all you can always decide that a submodel in your component has grown big enough to earn his place as a separate library (or embedded component). It's always a possibility, so keeping it mind (until it goes against other considerations deemed more important) while designing the package structure could make further refactorings and maintenance easier.

I hope these unorganized ramblings have made some sense and we can try another approach.

Solution 4

 (ContentDownloader) -> (Compressor) <- (ZipCompressor, RarCompressor)

Seems much better. Adding another implementation only requires recompiling (and releasing) (ZipCompressor, RarCompressor) and nothing else. We reached the point I've been heading to, but actually there is another interesting variation.

Solution 5

 (ContentDownloader,Compressor) <- (ZipCompressor, RarCompressor)

This structure can be justified by pointing out that the ContentDownloader directly depends on the Compressor, so for the sake of package cohesion it might be a good idea to put them the together. This is what Martin Fowler calls Separated Interface. After all for the developer of the ContentDownloader the implementation of the Compressor is an irrelevant detail he doesn't even want to know about. But code change in the ContentDownloader would lead to recompiling  (ZipCompressor, RarCompressor), so for now I drop this solution.

Drawing conclusions

What happened here is we started with an initial state of (if we don't count solution 0)

(ContentDownloader) -> (CompressorZipCompressor, RarCompressor)

and transformed it to

(ContentDownloader) -> (Compressor) <- (ZipCompressor, RarCompressor).

We had a depender and a dependee package. We extracted the visible part of the dependee package into a new package and both the depender package and the remainder of the original dependee package depends on that now.
Doesn't it resemble something? If we replace the word "package" to "class" and "visible part" to "interface" what we get is the appliance of the DIP (Dependency Inversion Principle). An example with classes and interfaces:

UserService ------uses------> UserRepository <----implements----- MongoUserRepository

And (not so) surprisingly there is an equivalent existing principle for packages, called SDP (Stable Dependencies Principle), coined by the same Robert C. Martin, who has come up with DIP (or SOLID). If you haven't heard of them yet, I strongly advise to follow the links above.

Back to the game, after reinventing the wheel, I'm quite content with the result. We have found a general principle not only to justify the "rightness" (from at least one point of view) of our chosen package structure at the end, but it might even give us some practical advices how to achieve it. To put it in one sentence, if we have the package structure

(A) -> (B)

then move all classes in B that are not referenced directly from A to a new package B2, rename B to B1 and the dependencies will look like

(A) -> (B1) <- (B2)

Of course this is a simplistic example with only 2 packages involved, but I think the general idea can shine through. And of course there is a lot of other important factors, package cohesion for example, we haven't really taken into consideration. In the following posts I want to explore those and investigate the "usefulness" of some package coupling metrics.