One web API route that needs multiple services entity-framework-core


I'm building a site that uses workflows. Basically, a workflow can have tasks and also an history of everything that happened in the workflow. When someone assign a task to someone else via the UI, two things must happen:

  • The task must be assigned to the new assignee
  • A workflow history entry must be created

So to do so, I have a "TaskController" Web API with a "Assign" route that expects the task ID and the new assignee username.

Now, I don't know exactly how to proceed. Indeed, I have a "TaskService" and I could add a method "Assign" into it but I'm not sure exactly what this method should do. I'm sure it needs to assign the task, but what about the creation of the workflow history? To me, it's the responsibility of a "WorkflowService" to do so. Moreover, there might be some cases where I don't want to create an history, but as I'm using "TaskService", it will always do so (except if I add a "createHistory" parameter to the method but I don't want to do that).

Another solution I have in mind would be to call these different services in the Web API route. Something like this (in pseudo code):


This seems a better solution but the problem is that these two methods write in the database, so I should use a transaction:

using (_dbContext.Transaction) {

This would work but I feel that it's a lot of logic for a Web API route...

I Googled this for a while and I didn't find anything that would help me take a decision. For me, a service should only do specific actions, so a "TaskService" should not add a workflow history entry. However, a Web API route shouldn't have a big logic in it (so, it shouldn't create a transaction).

Does anyone know a pattern or something that would help me with this?

2/20/2019 3:16:23 PM

Accepted Answer

You're asking a question that cannot be definitively answered. It's all up to how you want to build your app, and thus a decision only you can make. There's no hard and fast right or wrong here.

Generally speaking, for low-level things like audit trails, you want those to be abstracted away. You shouldn't have to think about adding something to the audit trail: it should just happen. That sounds like an argument to integrate it into your task service (and any other services that need this functionality). There's two things to note, though. First, it can still be a "workflow service", it's just that only other services interact with it, not your application itself. Second, a service doesn't have to just do one thing in the absolute most strict way. Your task service is "doing one thing" in that it only works with tasks, but it's perfectly acceptable for it to enlist other services in the furtherance of that goal.

That said, there's nothing technically wrong with your alternate approach of calling each service independently in your action. Although, coordinately a database transaction will be difficult if the services are truly independent, and if they aren't then you're not building good services. This is actually another reason for looking at the scope of a service more holistically. If adding a task requires adding a workflow, and those things need to be done together transactionally, then you have to do them in the same method, which means adding workflows items is a function of your task service, at least in an abstracted way.

As far as patterns go, microservice architecture is basically what you're building here, only without creating separate apps. However, following a true microservices architecture will force you to truly delineate the scope of your services. If you look at the pattern, you'll see that each microservice is a discrete unit of functionality, but not forbidden from interacting with other services to coordinate the work. For example, a cart service might need to work with an inventory service and a pricing service. It's still doing just one thing: managing carts. However, there's other work that has to be done to make that happen - work which other services are responsible for. Therefore, they must coordinate to get the job done.

2/22/2019 2:15:27 PM

Popular Answer

In your scenario, you can create the history using two ways

  1. By using database triggers
  2. By overriding DatabaseContext.Save() method for your task entity in EF repository

This way, you will only need one call to the "_taskService.AssignTask(...)" from your Web API method, and the history will be created. Choosing audit trail method will depend upon your application architecture(i.e., how you have created your services and repositories, your entities, etc.). If your application design is not too complex, either of the above two methods will work seamlessly.

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow