Refactoring legacy system to hexagonal architecture

In this article I am describing transformation step how to change a legacy system to more flexible hexagonal architecture by following the Single Responsibility Principle .

To avoid any difficult to understand abstraction I am using specific example from the domain I working on currently.

Domain action description

We are creating meetings. The meeting is a very complicated entity. Here it is a simplified version of it:

I think it is clear that even such a simple action as creating a meeting is not a trivial “few-liner” code to implement.

Worst possible architecture

One of the so called “service” methods is createUpdateDelete function which is taking care of… (well) the creation, update and delete operation in one big chunk.

As this is only one of method of the service object this is under continuous change and some of the changes has side effect so even if no creation operation is changed it becoming behaving incorrectly.

For short it is violating the Single Responsibility Principle .

Step 1: Operation object

Very first step is to extract method into separate object. Now that object is encapsulation all the statement required to perform the job.

NB: I am not describing any of the primitive Refactoring steps to implement this change: move method to separate object, move all the dependencies too, delegate original method to the new object method.

Then split the object into three different object per operation: CreateMeeting, UpdateMeeting, DeleteMeeting.

Now let’s focus on one of the operation only: CreateMeeting

I am sure that it is already better

What kind of responsibilities CreateMeeting has?

So we still have too many responsibility. Can you estimate how many test case it need to test? (trick: is has the “fact” in the formula :) - Oh yes it is exponential to the number of component working together)

Step 2: split responsibilities

Let’s regroup responsibilities:

Hmm… It has slightly different shape. I think we could identify a few of the additional object which could be used to decompose CreateMeeting.

At the end I could come up the following list of object: ValidateMeeting, MeetingSave, ParticipantSave, TechnicalNeedSave, InterpretationNeedSave, LocationSave

Each of the these objects are responsible for mapping to db entities and inserting into db (through repositories/daos). The CreateMeeting object now have a single responsibility: orchestrating the create meeting operation. All other (lower level) responsibility is delegated to the appropriate object identified above.

And as a side effect CreateMeeting is a hexagonal cell. And hexagonal architecture is well separated from each other and make unit testing amazingly easy.

You could say “But LocationSave has more than one responsibility: mapping to db entities and calling repository for inserting into db”.

My answer:

May 27, 2014
comments powered by Disqus