1

In the following example, the AccountService and ProductService are in an ASP.NET MVC app. The AccountWebAPI and ProductWebAPI are externally hosted API micro services.

1) Can I eliminate the ProductService and orchestrate the retrieving of the orders in the CustomerAccountController itself? This is because I consider the Controller as the Application layer/service which is mentioned in the DDD (Domain Driven Design).

2) Am I violating the n-layer architecture because the ProductService calls the AccountService which is the same layer?

3) Since AccountWebAPI and ProductWebAPI are micro services, do they have to be separated as AccountService and ProductService in the client application (MVC App) also to keep the Separation Of Responsibility? So the ProductService needs to be renamed as ProductAppService and ProductService should interact with ProductWebAPI only like AccountService talks to AccountWebAPI.

public class CustomerAccountController : Controller 
{ 
    IProductService _productService;

    public CustomerAccountController(IProductService productService)
    {
        _productService = productService;
    }

    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Account(int customerId)
    {
        var orders = _productService.GetOrders(customerId);

        return View(orders);
    }
}

public class ProductService 
{ 
    IAccountService _accountService; 
    IProductWebAPI _productWebAPI;

    ProductService(IAccountService accountService, IProductWebAPI productWebAPI)
    {
        _accountService = accountService;
        _productWebAPI = productWebAPI;
    }

    IList<Order> GetOrders(int customerId)
    {
        // Find the International Customer Number for CustomerId
        object customer = _accountService.GetInternationCustomerInfo(customerId);

        // Convert the string type to int
        var modifiedCustomerNumber = Convert.ToInt32(customer.Id);

        // Get the orders
        return _productWebAPI.GetOrders(modifiedCustomerNumber);
    }
}

public class AccountService 
{ 
    IAccountWebService _accountWebAPI;

    CustomerService(IAccountWebService accountWebAPI)
    {
        _accountWebAPI = accountWebAPI;
    }

   object GetInternationCustomerInfo(int customerId) 
   { 
        return accountWebAPI.GetCustomer(customerId) 
   } 
}

UPDATE: I realized that OrderService would be the appropriate service name for orders and not ProductService.

The LAYERS:

VIEW -- CONTROLLER -- SERVICE -- WebAPIs -- DOMAIN -- REPOSITORY

OrderView -- CustomerAccountController -- ProductService (calls AccountService in the same layer) -- ProductWebAPI -- ProductDomain -- ProductRepository

wonderful world
  • 10,969
  • 20
  • 97
  • 194

2 Answers2

6

The names AccountService and ProductService imply that you are violating the Single Responsibility Principle, Open Closed Principle and Interface Segregation Principle. Together, those three principles are 60% of the SOLID principles.

The reasoning for this is explained in this article, but in short:

The Single Responsibility Principle is violated, because the methods in each class are not highly cohesive. The only thing that relates those methods is the fact that they belong to the same concept or entity.

The design violates the Open/Closed Principle, because almost every time [a method] is added to the system, an existing interface and its implementations need to be changed. Every interface has at least two implementations: one real implementation and one test implementation.

The Interface Segregation Principle is violated, because the interfaces [such as IProductService] are wide (have many methods) and consumers of those interfaces are forced to depend on methods that they don’t use.

The solution is to give each use case its own class. This design is explained in detail here and here.

I would even say that having Web API controllers with the same structure leads to the same kind of SOLID violation. In fact, if you apply the design given by the articles, you can completely remove all your Web API controllers, and replace them with a single piece of infrastructure logic that will be able to pass messages around. Such design is described here (the article mainly talks about WCF, but its applicable to Web API as well and a working example of Web API can be seen in the example project that the article links to).

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Very good articles. But they are not on the line of CQRS applied on the DDD principles? The example that I provided does not have CQRS, but separated by 'service' like account service provides account functionality and product service provides customers orders functionality which is both read and write. – wonderful world Nov 01 '15 at 12:25
  • 1
    @wonderfulworld those articles are not about CQRS, they are purely about SOLID. Even if you don't want to make the strict distinction between reads and writes (which I think you should), you should at least give each use case its own class. Stuffing all entity related operations in one single class is ALWAYS one huge SOLID violation. – Steven Nov 01 '15 at 12:57
  • Let us take three features on the UI in the MVC app (1) Account Page (2) Order Page (3) Rewards Page. In this case, in my MVC app should I have an account service whose responsibility is to bring in the account details and an order service (product service in my example) and (3) **RewardService** that bring in the reward details? Assume I don't have a **RewardsWebAPI** service but the rewards are provided by the **ProductWebAPI** (or OrderWebAPI), but I will still have RewardService and not pollute the ProductService with a **GetRewards** call. – wonderful world Nov 01 '15 at 13:27
  • 1
    @wonderfulworld you should not have any XXXService class at all. I advice you to reread the referenced articles again. – Steven Nov 01 '15 at 14:07
3

1) Can I eliminate the ProductService and orchestrate the retrieving of the orders in the CustomerAccountController itself?

You could do that, but that means you would mix up delivery logic with applicative logic. It's not the worst SRP violation but that would remove the option of adding a second delivery mechanism (something else than Web API) for the same use case. It can be a valid tradeoff in some circumstances though.

2) Am I violating the n-layer architecture because the ProductService calls the AccountService which is the same layer?

Absolutely not. An architecture is a set of constraining technical decisions that were made. The only way you could violate an architecture would be to set up a second, parallel architecture that somehow breaks principles from the original one. Here, you wouldn't even violate the n-layer approach since nothing in it says that you shouldn't call someone in the same layer.

3) Since AccountWebAPI and ProductWebAPI are micro services, do they have to be separated as AccountService and ProductService in the client application (MVC App) also to keep the Separation Of Responsibility? So the ProductService needs to be renamed as ProductAppService and ProductService should interact with ProductWebAPI only like AccountService talks to AccountWebAPI.

Your question suggests that the use of microservices might not be a thought out, educated choice here. Microservices are separation of responsibility taken to the extreme. They should be independently deployable and share as few things as possible. I also suggest you model your subdomains and Bounded Contexts (big business areas) first. Microservices will naturally fall into one of the BCs.

Community
  • 1
  • 1
guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • 1
    Just to clarify on item 3, I was trying to find out whether or not the MVC app which is primarily the application or client has to follow the same separation that the WebAPIs follow also. Please see the Layers diagram that I added in the original question. As an example, if there is a **ProductWebAPI**, there should be a **ProductService** in the MVC app also. – wonderful world Nov 02 '15 at 11:23
  • 1
    I would move the Service layer one position to the right in your schema so that it stands between WebAPIs and Domain (which is what my answer to 1) was about). – guillaume31 Nov 02 '15 at 12:36
  • 1
    For writes, your MVC controller will be a dumb pipe to the microservice. For reads, it might want to aggregate data from multiple microservices, but this is still presentation logic so I wouldn't put it in an (Application) Service. You can also have data on your view coming from multiple MVC controller Actions each connected with a specific Microservice. – guillaume31 Nov 02 '15 at 12:43
  • I agree with you that there should be a service layer between the WebAPIs and Domain. I believe that is the **Domain Service** layer. Am I right? – wonderful world Nov 02 '15 at 12:46
  • Not having any **service** (ProductWebAPI or AccountWebAPI) in the MVC and the controllers talking to WebAPIs directly is what I preferred in the point 1. As a result the separation of concerns of the WebAPIs are reflected in the client app also. – wonderful world Nov 02 '15 at 12:49
  • 1
    It isn't the Domain Service layer but the Application layer. An Application Service is aware of the current use case execution progress. It controls the business transaction (typically through a Unit of Work) and orchestrates calls to the Domain. – guillaume31 Nov 02 '15 at 12:54
  • 1
    Maybe you should ask 1) again in a new question without mentioning a Service or DDD. Service is an overloaded term that can lead to a misinterpretation of what your problem is really about. DDD doesn't come into the equation until way inside the microservice. Interactions between the MVC controller and the microservice have nothing to do with DDD IMO. – guillaume31 Nov 02 '15 at 13:02