Convert Or Construct? It Depends.
So far, we have learned about arrows of dependency and in which direction they should point. Today’s article is the final instalment on dependency management.
We’ll carry on with the CustomerController.Register()
example.
Below is the Register()
method on CustomerController
as we left it last time.
private IRegisterCustomerUseCase RegisterUseCase { get; set; } [HttpPost] public async Task<IHttpActionResult> Register(ApiCustomerRegistration apiCustomerReg) { Validate(apiCustomerReg); var reg = apiCustomerReg.ToRegistration(); var customer = await RegisterUseCase.Register(reg); return Ok(customer); }
Let’s change the scenario: The customer model we are emitting in the HTTP response can no longer be the Customer
instance returned by RegisterUseCase.Register()
. Possibly Customer
contains some sensitive data we need to strip out.
We decide to return a different model, ApiCustomer
. ApiCustomer
intends to present data differently to clients; it is a presentation logic construct. We convert Customer
into ApiCustomer
:
[HttpPost] public async Task<IHttpActionResult> Register(ApiCustomerRegistration apiCustomerReg) { Validate(apiCustomerReg); var reg = apiCustomerReg.ToRegistration(); var customer = await RegisterUseCase.Register(reg); var apiCustomer = customer.ToApiCustomer(); return Ok(apiCustomer); }
Did we do well? No, not with regards to preserving our one-way dependency of Presentation Logic on Business Logic.
Why not? The dependency graph shows us why:
The arrow of dependency points from Customer
to ApiCustomer
! The Business Logic’s Customer
class is aware of ApiCustomer
, a Presentation Logic class. That is wrong. ApiCustomer
can know about Customer
but not vice versa.
How do we change line,
var apiCustomer = customer.ToApiCustomer();
to something where Customer
does not need to be aware of ApiCustomer
?
How about this?
var apiCustomer = new ApiCustomer(customer);
We could have a constructor on ApiCustomer
that takes in a Customer
instance and spits out an ApiCustomer
instance. Now ApiCustomer
knows of Customer
, but Customer
can be oblivious of ApiCustomer
. Much better!
The CustomerController.Register()
changes to
[HttpPost] public async Task<IHttpActionResult> Register(ApiCustomerRegistration apiCustomerReg) { Validate(apiCustomerReg); var reg = apiCustomerReg.ToRegistration(); var customer = await RegisterUseCase.Register(reg); var apiCustomer = new ApiCustomer(customer); return Ok(apiCustomer); }
We have reversed the arrow of dependency between Customer
and ApiCustomer
. Once again, all dependencies crossing between components (the blue vertical line) point towards Business Logic:
Leave a Reply
Want to join the discussion?Feel free to contribute!