Working with a legacy application, I tries to create new features as much as possible isolated from the legacy code. When you are in the same application, in a monolith, it is way too easy to just add code and use functions and other services from all over the place. After all, why not? We have access to it, it’s part of the same application. The problem with not respecting boundaries, is that your logic starts to be spread everywhere, and touching one part of the code has consequences on other parts.
Recently, I’ve tried to introduce some sort of bounded context that are isolated from the rest of the application. Concretely, it means that:
- the outside world cannot access my domain models directly;
- the inside world of my bounded context cannot access the outside world directly
Instead, if I need something from another bounded context (I considere the whole legacy app as one bounded context), I treat that like a third party service. So the thing I get back is transformed into something from my bounded context.
My New Bounded Context™️ needs to use the User (or at least some part of it) that is defined in the Legacy App. So instead of simply fetching the User (from the db or whatenot) and using it as I would do in the Legacy App, I introduce a User domain model (that is different than the other User) of my New Bounded Context, and a UserClient with an implementation that takes care of:
- Fetching the Legacy App User
- Transforming the Legacy App User into the New Bounded Context User
And now, within my New Bounded Context, I have a brand new User that contains only the fields and methods that I care about in this context. The other advantage is that if someone decides to change the User of the Legacy App, it won’t affect my New Bounded Context
It can seem counter-productive and expensive to introduce an Anti-Corruption Layer when you are in the same application, but it makes you go faster in the long run since there is less coupling, changes are more isolated, and therefore safer to do.