質問

As you can see in the picture below I'm working on a proof of concept of an architecture design that will work for my research group's needs. These are:

  • Reusable business logic components
  • Decoupled data model
  • Decoupled front-end(s) (i.e. a web ui, a service, a console app, etc.)
  • All components (except for front-end) should be completely unit testable (using e.g. mocking).

In order to achieve this we introduced a "glue" component (Glue.ConsoleApp) which is basically a Facade pattern implementation. Every front-end will have a corresponding facade. Here's a simple C# solution that shows a very minimal implementation of separated components.

Overview

The facade links all the components together but the code is still too complex in my opinion. I'm struggling with the fact that in order to separate all the layers completely, they all need to define their own entities to work with, and the glue layer has to do tons of mapping (which is why I use ValueInjecter, a library that automates most of this work).

Here's an example method that shows the complexity of having three different classes that all represent an InvoiceLine in one method:

using Database = ArchitecturePoC.DataAccess.Database;
using InvoiceProcessing = ArchitecturePoC.BusinessLogic.InvoiceProcessing;

public static Dictionary<Entities.Invoice, List<Entities.InvoiceLine>> GetAllInvoicesWithInvoiceLines()
{
    Dictionary<Entities.Invoice, List<Entities.InvoiceLine>> result = new Dictionary<Entities.Invoice, List<Entities.InvoiceLine>>();

    Database.InvoiceMapper invoiceMapper = new Database.InvoiceMapper();
    Dictionary<Database.Entities.Invoice, List<Database.Entities.InvoiceLine>> invoicesWithInvoiceLines = invoiceMapper.GetAllInvoicesWithInvoiceLines();

    foreach (KeyValuePair<Database.Entities.Invoice, List<Database.Entities.InvoiceLine>> invoiceWithInvoiceLines in invoicesWithInvoiceLines)
    {
        List<Entities.InvoiceLine> subResult = new List<Entities.InvoiceLine>();

        foreach (Database.Entities.InvoiceLine invoiceLine in invoiceWithInvoiceLines.Value)
        {
            Entities.InvoiceLine resultInvoiceLine = new Entities.InvoiceLine();
            resultInvoiceLine.InjectFrom(invoiceLine);
            subResult.Add(resultInvoiceLine);
        }

        Entities.Invoice resultInvoice = new Entities.Invoice();
        resultInvoice.InjectFrom(invoiceWithInvoiceLines.Key);

        result.Add(resultInvoice, subResult);
    }

    return result;
}

I'm worried that I'm mistaking this "clean separation" for something that will eventually give me more worries than I would like. I can imagine the facade quickly growing very large and hard to maintain. Do you have suggestions for reducing this complexity?

Side note: I've looked at the domain event pattern which looks interesting, but I'm not sure how to apply it to this situation.

役に立ちましたか?

解決

The problem I see with your architecture is the 3 different Entities folders. To me an entity is a business/domain related concept, they should be defined in the business layer only. In contrast, I tend to call objects that are shared between the service and presentation layer DTO's. They can be an exact reflection of business entities, or something more partial or more elaborate. As for the Data Access Layer, if you use an object relational mapper it will allow you to persist entities from the business layer transparently, you don't need to declare DAL versions of your entities. What you could have in the DAL is DAO's or Repositories named after your entities, but they aren't entities, they manipulate them.

This doesn't remove all the mapping, but I'm afraid in a layered architecture some amount of mapping has to be done anyway.

As a side note, I can't see why you should have a Facade project for each font end. This would mean a lot of code duplication. Usually applications have some kind of Service layer that serves as a single Facade to business operations for all presentation layers.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top