Anticorruption Layer — An effective Shield
The first time I heard about the term Anticorruption Layer ( ACL ) was in Eric Evans´s Book “Domain Driven Design. Tackling complexity in the heart of software” (https://www.amazon.es/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215).
Those days, DDD was a new frontier that I was very excited about all those new concepts but I didn’t achieve to land much of those concepts.
In recent years, in almost all developments I have done, I had to deal with legacy code , data repositories or “third party” subsystems and ACL has been taking “shape” and special attention must be paid in it when we deal with other subsystem.
In wiki.c2.com ( http://wiki.c2.com/?AnticorruptionLayer ) we could find a definition of ACL:
“If your application needs to deal with a database or another application whose model is undesirable or inapplicable to the model you want within your own application, use an Anticorruption Layer to translate to/from that model and yours”
In his book, Evans writes two sentences that believe are very interesting.
“But when the other side of the boundary starts to leak through, the translation layer may take on a more defensive tone.”
“When systems based on different models are combined, the need for the new system to adapt to the semantics of the other system can lead to a corruption of the new system’s own”
The underlying concept is pretty clear. When we need to “talk” with other system, data repository or legacy code ( even “ours” legacy code ) we should prevent our model from other “foreign models”.
We must consider these external systems or data repository as differents Bounded contexts and of course, it will have their own model a will have a relationship with ours.
Most of the time, this mapping will be customer/supplier kind and we usually will be the conformist part.
The ACL is a very good “tool” even if you aren`t implementing DDD on your developments.
In his book “Domain-Driven Design Distilled”, Vaughn Vernon says:
Even if you are able to avoid creating a Big Ball of Mud by employing DDD techniques, you may still need to integrate with one or more. If you must integrate with one or more, try to create an Anticorruption Layer against each legacy system in order to protect your own model from the cruft that would otherwise pollute your model with the incomprehensible morass.
As you can see, the ACL not only were a simple translator layer. If we develop an ACL we should archive:
- Translate model
- Protection against failure
- Improve our integration testing
Ok, enough theory! Lets hands on work!
Imagine that we are developing a banking related application. And we need to deal with credit card appended to an account.
To fetch this information, we need to talk to an old and not much reliable SOAP server to retrieve credit cards information appended to an account.
There are a lot of ways of implements an ACl layer but always we work with very commons design patterns like facade , adapter and translator.
Here is our model. We have a basic credit card information and we could validate it with some well known algorithm.
Here we have a service that recover the credits cards from the SOAP Server
As you can see in the code, we recover the credit card information and translate to our model.
The point here is, we always will provide an account number and we recover a list of the credit cards appended to this account or if something were wrong we would capture an Exception
If the third party systems changes we only need to change our translators or even if the services protocol is changed we only need to modify one class to keep our model integrity.
With these design we prevent any “foreign entity” slice in our model.
Ready for failures
What happens to our application if the credit card server falls? Would we see an error 500 on our application? Maybe a warning page are more confortable to our users or hide this part of the page information and show the rest or we could use a “cirtuit breaker” and provide a cached response.
Maybe you´ll need more than one king of exception to add more granularity to failures (ie CardServiceConnectionError , CardServiceTranslationError)
Improving our service
The existence of this abstraction layer allows us to decorate it andadd more features this in order to increase the defenses of our system. Probably we need to log all transactions in order to have some audit. Probably, we could measure time response or response code and send all telemetry to a ELK Stack.
Let´s decorate our service:
In my experience ,this king of actions could be a good practice when we are developing a “frontend” because we are the visible part of the iceberg. Most of time, monitoring the subsystems would help us to detect the cause of an error in our application.
Only Mock Types That You Own. Don’t Mock Types You Can’t Change
Growing Object-Oriented Software, Guided by Tests
Steve Freeman Nat Pryce
When we create a integration test, we need to do it from a well known system state. For example, if we want to test the integration between a repository and a database we need firts we need to create table, populate with some concrete data, execute the test and check that database state has been changed in the way we had expected.
Nowadays, replicate a database y pretty easy using virtualization( like Docker ) and we could run it in our integration tests in CI pipelines.
This could be more complicated or even impossible if the system is not under our control and we couldn`t ensure we always have same state when we run our test.
A ACL allow us to mock this system and this way, we could test our code reacts in front of failures.
We could mock the subsystem response and test how works our bounded context with this new partner.
In conclusión. With a ACL we will grant:
- Translate information
- Protect from other subsystem failures
- Log and monitorization relationships
- Good integration testing