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:

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply