Follow Arrows Of Dependency
Dependency management seems like an abstract nice-to-have. Far from it. When we neglect our dependencies, we set ourselves up for failure—a future of convoluted, rigid, fragile code.
Today we are continuing our journey into dependency management. We want to design our systems purposefully to minimise dependencies. We’ll discover that types can be aware of some types yet be oblivious to others.
Let’s look at an example: Say, we have a Web API component and a Business Logic component. Yesterday we discovered that the arrow of dependency should point from the Presentation Logic (here Web API) to the Business Logic.
The Web API component contains a CustomerController
class that captures customer-related HTTP requests. One controller action, Register()
, on CustomerController
is concerned with registering new customers in the system:
private IRegisterCustomerUseCase RegisterUseCase { get; set; } [HttpPost] public async Task<IHttpActionResult>Register(ApiCustomerRegistration apiCustomerReg) { Validate(apiCustomerReg); var customer = await RegisterUseCase.Register(apiCustomerReg); return Ok(customer); }
In short,
- we convert the HTTP POST payload into an
ApiCustomerRegistration
instance, that - is validated, and
- passed into the use case to register the customer in the system, which
- returns us a
Customer
that we emit in a 200 – OK HTTP response back to the client.
This flow sounds reasonable. But there is something wrong. It’s a biggie, and it’s hard to notice.
Maybe it’s easier to see when we depict the dependencies between objects in a UML diagram:
Note: Open arrows mean ‘úses’ while closed arrows represent implementation.
Take a minute to see whether you can spot the problem with one of the arrows.
Below is the same diagram but with the offending arrow highlighted in red.
It’s the only arrow crossing from BL to PL.
Why is this arrow problematic? Because it implies a dependency of Business Logic (BL) on Presentation Logic (PL). We had established Business Logic was to be independent of Presentation Logic. Yet here we go and make the BL depend on PL!
In our code, this line is the offender:
var customer = await RegisterUseCase.Register(apiCustomerReg);
The Business Logic’s use case class takes a presentation logic parameter, apiCustomerReg
, and must depend on the Web API. Dependency problems can be hard to spot, yet have far-reaching consequences.
OK, how do we fix this?
We convert the ApiCustomerRegistration
to a CustomerRegistration
. CustomerRegistration
will be a Business Logic type. And even though CustomerRegistration
cannot be aware of ApiCustomerRegistration
, the reverse is not true. Let’s see the code:
[HttpPost] public async Task<IHttpActionResult> Register(ApiCustomerRegistration apiCustomerReg) { Validate(apiCustomerReg); var reg = apiCustomerReg.ToRegistration(); var customer = await RegisterUseCase.Register(reg); return Ok(customer); }
We convert the ApiCustomerRegistration
to a CustomerRegistration
and pass it into the use case’s Register()
method.
What does that look like in the UML diagram?
Problem fixed. All dependencies are pointing from Presentation Logic to Business Logic.
Please follow arrows of dependency and ensure they point in the same direction between components. You’ll avoid problems in the future.
Leave a Reply
Want to join the discussion?Feel free to contribute!