Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Communication between services #1

Open
pfaustinopt opened this issue Apr 26, 2019 · 6 comments
Open

Communication between services #1

pfaustinopt opened this issue Apr 26, 2019 · 6 comments

Comments

@pfaustinopt
Copy link

Hey,

I have a question about Acme.Core layer, most specifically the Acme.Core.Service.

Based on your example and on code that I see around the internet, it's rare to see two services communicating between themselves.

Imagine you've service A and Service B, and you need to create a method on Service B that has some logic that is already implemented by a method on service A.

How do you deal with this? Do you add a reference to that service and invoke the corresponding method or do you repeat the logic on the service that you are working on?

I have the idea that each service method should map to one business use case. This means that it is not recommended to have methods calling each other, because they work independently and each one of them solves a specific use case (the method you are calling might invoke unitOfWork.Commit(), meaning your pending changes will be saved without you knowing it, or you can have your method inside a transaction and the method you're calling might take too long to run).

I'm a little bit confused about this topic because on one side I want to promote code reuse but then I start thinking about all the problems that might occur with DbContex, TransactionScopes, etc.

@cwilby
Copy link
Owner

cwilby commented Apr 27, 2019

If I found myself sharing functionality between services I'd imagine that logic wouldn't belong in A or B. You could extract the functionality required in A and B into a new service C, and inject C into A and B.

Thoughts?

@pfaustinopt
Copy link
Author

pfaustinopt commented Apr 27, 2019 via email

@cwilby
Copy link
Owner

cwilby commented Apr 28, 2019

Yes - UnitOfWork should be refactored to use a similar pattern to this, making UnitOfWork a transient service instead of in request scope.

@pfaustinopt
Copy link
Author

Oh, I see. Creating multiple UnitOfWorks and having each one access to a group of repositories.

However, we don't want to put our business logic on repositories as they should only be used to abstract the access to the database, meaning that we still want to keep the logic on the services layer. I don't think that the link you provided would solve this problem (I might be wrong).

Imagine this:
On your example, you have Customer and Order as domain models and you created services that are responsible for the business logic associated with these. We could have a new requirement like this: "When processing a new order, if the customer is not already on the database create a new one". To avoid repeating the logic of creating a new Customer, we'd invoke the Create method on the CustomerService:

public CustomerDTO.WithRelations Create(CustomerDTO.WithRelations customer)
{
var dbCustomer = new Domain.Customer.Customer(customer);
customer.Phones.ForEach(cp => dbCustomer.Phones.Add(new CustomerPhone(cp)));
customer.Emails.ForEach(ce => dbCustomer.Emails.Add(new CustomerEmail(ce)));

_customerRepository.Add(dbCustomer);

_unitOfWork.Commit();

return _mapper.Map<CustomerDTO.WithRelations>(dbCustomer);
}

_unitOfWork.Commit() cause the side effects I was talking previously. By using Microsoft implementation of UnitOfWork, to solve this problem we'd need to this logic on the repository itself. Otherwise, we'd face the same problem.

@pfaustinopt
Copy link
Author

Yes - UnitOfWork should be refactored to use a similar pattern to this, making UnitOfWork a transient service instead of in request scope.

I was looking again at the UnitOfWork registration on the container:

container.Register<IUnitOfWork, UnitOfWork>();

Doesn't this translates to the UnitOfWork being a transient service?

@cwilby
Copy link
Owner

cwilby commented Apr 29, 2019

In the first case, you could add FirstOrNew / FirstOrCreate methods to the abstract repository layer, and utilize DbCollection.FirstOrDefault in the concrete implementation, then utilize that method in your service.

I'm using Laravel for another project at the moment, and the firstOrNew method comes in handy for this case, and is encapsulated in the "model" layer.

UnitOfWork itself is a transient service, however the DatabaseFactory it depends on to create a database connection is in request scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants