0

I am working on an N-Layered .NET application with the following layers:

  • Presentation
  • Application
  • Domain
  • Infrastructure (Contains Persistence and common utility functions like email)

At some point in my application, a request is approved. Once approved, there are 4 steps which must be performed as follows:

  1. Assign a product tag code to the issued product in the Request.
  2. Update the Request status from “Being Processed” to “Order Completed”
  3. Send an email to the requester stating their product is ready for pickup
  4. Send an email to the requester’s manager informing them of what their employee has been given and a copy of the request they approved for the product

The above steps must be part of an atomic operation meaning they must all be done or the action is cancelled. I am thinking of having the Application Layer directing the Request Repository and Persistence Layer to carry out the tasks as follows:

  1. Application Layer requests for the Request Repository to perform steps 1 and 2 as a unit of work transaction
  2. Application Layer requests for the Infrastructure layer to carry out steps 3 and 4.

I'm not exactly sure if the Application Layer is supposed to do this. I think it is because the first 2 actions require contacting a repository which I have avoided doing in the Domain Layer. The last 2 steps involve the Infrastructure Layer which the domain layer may talk to via a dependency injected instance. Following this logic, the Application Layer is not asking the Domain Layer to do anything. Is this typically how it is for a multi-step process like this when you have an Application Layer? Or did I miss something here?

I am aware of a framework called Windows Workflow but I'm not sure if that would help in this case as this multi-step process does not involve human interaction along the processing steps where things could be waiting for a few days. I also don't want to make the application overly complex if I don't have to.

Thanks in advance.

Robertcode
  • 911
  • 1
  • 13
  • 26
  • You are probably looking for a Saga in this case, since you seem to have 2 different aggregates in play ,`Product` and `Request`. – plalx Feb 08 '16 at 04:06
  • Thanks for the problem identification. Looks like I miss stated the situation. There is a product table which only contains records for each unique available product. In this case, there are only 4 possible cell phone models which can be requested. When a request is completed, it is updated with the model of phone that was issued along with a product tag code. Product records are not updated. The question still stands if the 4 steps are carried out by the Application Layer. I will restate the first step. I'll also change Product Repository to Request repository. – Robertcode Feb 08 '16 at 06:07
  • Well then you can simply put the entire process in an application service. Application services are meant to coordinate business processes. Just load the `Request` AR from the repository, invoke business operations on it, save it back and send emails once the transaction is complete. If you implemented domain events you may also send emails in an event subscriber. – plalx Feb 08 '16 at 06:35

1 Answers1

4

Steps 1 and 2 in your question sound like domain logic to me. So no, it is not okay. These Two steps should be carried out in the domain. You can do the following:

  1. In the application service, load the relevant aggregate (probably the Request).

  2. Either invoke the operation that does step 1 and 2 on the Request, or (if the operation does not really belong to the Request) invoke a domain service that performs these two steps.

  3. Once the domain operation is completed, the application service can save the modified Request back to the DB.

  4. If the above steps are successful, you can send now the emails. You should have some kind of retry mechanism in place, so that the email gets sent eventually, even if there is a temporary problem.

If the changes in step two modify more than one aggregate, the process becomes slightly more complex. Sagas as mentioned in the comments are one solution to this problem. Still, the individual changes are domain operations, so model them accordingly.

As suggested by plalx in the comment, the sending of the emails can be offloaded to a domain event consumer if you have an eventing infrastructure in place. This usually solves the retry mechanism for free.

theDmi
  • 17,546
  • 6
  • 71
  • 138
  • To put the control into the Domain Layer as you suggested, I think I would like to create an interface in the Domain Layer to an Application Layer service which does the actual work. The service contract will be defined in the Domain Layer but implemented in the Application Layer. Basically, the business process order will be controlled by a Domain Service using an injected Application Layer service. I still have to look into the domain event consumer and event infrastructure that has been suggested. Thanks for the information. – Robertcode Feb 18 '16 at 17:03