Anatomy of an Anti-Corruption Layer, Part 2
This is part 2 of the series on Anti-Corruption Layers. If you missed part 1, go take a gander at it.
First, as I predicted, the Big Harry Design Up-Front was not the design that ended up being implemented. Fortunately, there were only minor changes, which are in red:
Some commentary on the code:
- First, the problem I mentioned in part 1: should Pen and Surface be friends of the translators in the anti-corruption layer? I decided that yes, this was acceptable.
My justification is, in my opinion, kind of weak: the ddd package already holds a reference to the anticorruption package. And if I keep the translators sufficiently generic, i.e., name them PenTranslator and SurfaceTranslator, then the domain objects Pen and Surface don’t “seem” as coupled to this specific ddd-to-legacy integration as before.
This implies that PenTranslator and SurfaceTranslator should really reside in a separate common package so they can be added to and used later on.
- The NewSystemDrawingServiceAdapter could have talked to the NewSystemPenDA and NewSystemSurfaceDA instead of the repositories directly.
- I admit that the legacy entities and the DDD entities aren’t that far apart in terms of data attributes. It’s only an example.
- For sanity’s sake, a real example might call for two parallel anti-corruption layers, one for each direction.
- Not shown in the design diagram is a Locator for the anticorruption package. It acts as a Registry, and not every single service or repository is found through this object. I only put objects there whose instantiation was either sufficiently odd or when I didn’t want the client class coupled to a concrete class (like with the adapters). Inconsistent I know, but it’s only an example.
- Some of the method signatures of the legacy stuff are intentionally bad — it’s legacy!
- Interestingly enough, the Pen and Surface repositories didn’t need to be friends of Pen and Surface because they delegate to the legacy services.
- I didn’t implement the count() methods in the two repositories — sue me.
- There are a lot of if-then-elses in the Locator and in the factories’ fluent interfaces — if this wasn’t toy code, I’d do it differently, of course.
All in all, for this tiny example, I think there might be a bit too much indirection in the anti-corruption layer itself. For both the legacy services that the ddd package uses and the new system service the legacy package uses, having these services talk to an interface that conforms to their bounded context might’ve made more sense if those services lived in their host packages.
As it is described in Evans, they are “border” services and live in the anticorruption package, when they conceptually belong to either the legacy or the ddd bounded contexts.