开发者

Sharing types in a modular application architecture

I have an application under development that is following a modular application design. There is a 'core' vertical stack that provides common, central services such as authentication/authorization, logging, data access, etc. Context-specific areas are implemented in modules that reference the core assembly in the same logical layer.

The application is a service app with a facade layer, domain layer and infrastructure code containing the data access code. Each module will implement a domain layer assembly. If the module has a public API, it will also implement a facade assembly.

For example, the solution contains:

  • Core.Api - Facade assembly for the core API
  • Core - Domain assembly for the core services, entities, etc.
  • Core.Data - Core data access assembly

     

  • ModuleA.Api - Facade assembly for module A (references Core.Api)
  • ModuleA - Domain assembly for module A (references Core)

     

  • ModuleB.Api - Facade assembly for module B (references Core.Api)
  • ModuleB - Domain assembly for module B (references Core)
  • Sharing types in a modular application architecture

    (and so on. We'll have about 24 modules when done.)

    All of this is bound together using DI with Unity as the IoC container. The WebHost project defines the configuration where the various implementations are mapped to the interfaces they fullfill.

    This approach is not simply for logical purposes but to allow us flexibility in implementation (i.e. we can change the implementation of a module without breaking or even touching the other code). It also makes it extremely ease to divide work across teams and to extend the application with new modules with little risk of regression error in other code.

    My question is what to do when ModuleB requires one or more types defined in ModuleA? Is it acceptable for ModuleB to reference ModuleA or do I need to move ModuleA into Core?

    UPDATE

    In looking at implementing some of the changes discussed below I ran into a gotcha that leads me to this clarification...

    There are two places where my modules are inter-dependent. Following the approach discussed below, I can move the interfaces and types I want shared from Module1 to Core which allows ModuleB to make use of them without tightly coupling ModuleA and ModuleB. However, I also need to use some of the data contracts defined in ModuleA.Api for the service operations in ModuleB.Api. This makes me question the design.

    Because of IP, I can't describe our exact use-case bu开发者_运维技巧t suffice it to say that ModuleA has an EntityA domain object. ModuleA.Api defines the EntityContractA data contract (DTO) that is exposed by the public API. ModuleB.Api has a method that requires EntityContractA as a parameter then delegates to a method in ModuleB that accepts EntityA as a parameter.

    Again, moving EntityA into Core solves the domain layer problem but doesn't help me with the facade layer. Moving EntityContractA into Core.Api makes the whole thing smell a bit.

    Here's an example what I need to accomplish:

    In ModuleB.Api.ServiceFacadeB:

    public EntityContractB FilterBy(EntityContractA contractA)
    {
        var entityA = Mapper.Map<EntityContractA, EntityA>(contractA);
    
        var entityB = domainService.FilterBy(entityA);
    
        var contractB = Mapper.Map<EntityB, EntityContractB>(entityB);
    
        return contractB;
    }
    

    where domainService is ModuleA.ServiceB:

    public EntityB FilterBy(EntityA entityA)
    {
        // Do work
    }
    


    The good news: what you describe is actually a good architecture with clear seperation of concerns and modularity patterns. It allows your teams to develop modules independent from each other and only have dependencies on each others interfaces (facade / API). This is a best practice!

    The (potential) bad news: If you would start by directly referencing Module1 from Module2, you will single-handedly break this beautiful architecture. The team creating Module1 only needed to care about keeping their interface stable, but because of the direct reference now also need to be careful with changing their implementation.

    My suggestion: If you need a type in Module2 that is now defined in Module1, make sure you move it into the Core API or the Module1 API classes. This formalizes the fact that your assembly is depending on those types and makes it clear they need to be kept stable.


    Module2 need to have reference of Module1. interdependence between modules doesn't make a case for moving pieces to common core code.


    Is it acceptable for ModuleB to reference ModuleA or do I need to move ModuleA into Core?

    No, I'd redefine shared types somewhere that wasn't ties to a specific module. Just make sure that wherever you define these shared types is free of dependencies.

    [Warning, self promotion:] In the 5-Layer Architecture view of the world, they'd go in the "Common" layer.

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新问答

    问答排行榜