문제

I have a WinForms application that I am hopefully going to be refactoring to be utilizing a DDD architecture. First, I am trying to really wrap my head around the architecture itself, I have Evans' book and I have Vernon's book and I find myself struggling with three scenarios I would face rather immediately in my application. I am afraid I might be over thinking or being too strict in my conceptual design process.

1.) Utilizing an example provided inside a Pluralsight tutorial on DDD the speaker made a point that different bounded contexts should be represented by their own solution. However, if I have a winforms app that is not service oriented (this will eventually change and a lot of this question becomes moot) this doesn't seem feasible. I am therefore operating under the assumption I'll be separating these into different projects/namespaces being vigilant there are no interdependencies. Is this the right way to think about it or am I missing something obvious?

2.) I have a navigation UI that launches other modules/windows that would belong in separate presentation layers of different bounded contexts. Think of the first window that would be open when you launched an ERP application. Since this doesn't fit cleanly within any particular BC how would something like this be properly implemented. Should this fall within a shared kernel?

3.) I have a Job Management bounded context and a Rating/Costing bounded context. It is part of the business process that when a Job is created its details are then rated. This has its own UI, etc, which I feel pretty good that this presentation still adequately falls inside the Job Management context. However, the actual rating process of these details definitely should not. I am not entirely sure how to communicate with the Rating/Costing context since bc's are to be kept separate from one another. I realize I could do messaging, but that seems to be overkill for a non distributed app. Each BC could feasibly self host some kind of API but again this seems overkill, although this would set the team up nicely for migrating to a distributed architecture later on. Finally, my last idea is having some kind of shared dependency that is an event store of sorts. I don't know if this is the same as Domain Events as those seem to have a separate concern in and of themselves. So, does that mean this would fall under a shared kernel as well or some other type of solution?

Thank you in advance.

도움이 되었습니까?

해결책

1) Guidance about BCs corresponding to solution is only guidance, not a hard rule. However, it does provide much needed isolation. You can still have this with a WinForms project. For example, suppose you have a BC called Customers. Create a solution for it and within it, create an additional project called Customers.Contracts. This project effectively houses the public contract of the BC which consists of DTOs, commands and events. External BCs should be able to communicate with the Customers BC using only the messages defined in this contracts project. Have the WinForms solution reference Customers.Contracts and not the Customers project.

2) A UI often serves a composing role, orchestrating many BCs - a composite UI. A stereotypical example is the Amazon product page. Hundreds of services from different BCs are required to render the page.

3) Again this seems like a scenario calling for a composite UI. The presentation layer can mediate between different BCs. BCs are loosely coupled, but there still are relationships between BCs. Some are downstream from others, some are upstream, or even both. Each has an anti-corruption layer, a port, to integrate with related BCs.

다른 팁

The feeling I get from these questions can be summarized as: "What is a sane approach to BC boundaries from a code artifact perspective? and How do I build a UI that both queries and commands several BCs?". It depends ...

Another, not yet mentioned approach could be to regard the UI as a seperate context. I doubt it's a very popular POV, but it can be useful at times. The UI could dictate what it needs using e.g. its own interfaces and data structures, and have each BC implement the appropriate interfaces (doing an internal translation). The downside is the extra translation going on, but then again it only makes sense when there is sufficient value to be reaped. The value is in keeping things simple on the UI side and not having to worry how and where the data is coming from or how changes affect each BC. That can all be handled behind a simple facade. There are several places this facade could sit (on the client or on the server). Don't be fooled though, the "complexity" has just moved behind yet another layer. Coordination and hard work still needs to be done.

Alternatively you may also want to look into what I call "alignment" of a UI with use cases exposed by a BC. As Tom mentioned, workflow or saga implementations might come in handy to pull this off when coordination is required. Questioning the consistency requirements (when does this other BC need to know about given information?) might bring new insight into how BCs interoperate. You see, a UI is a very useful feedback loop. When it's not aligned with a BC's use case maybe there is something wrong with the use case or maybe there is something wrong with how it was designed in the UI or maybe we just uncovered a different use case. That is why UI mockups make such a great tool for having discussions. They offer an EXTRA view on the same problem/solution. Extra as in "this is not the only visualization you should use in conversations with a domain expert". UX requirements are requirements too. They should be catered for.

Personally I find that when I'm discussing UI I'm wearing another hat than when I'm discussing pure functionality (you know, things that don't require a UI to explain what the application is doing/should do). I might switch hats during the same conversation just to find out malalignment.

First things first, as I saw you talking about a message bus, I think we need to talk about BC integration first.

You do not need a message bus to communicate between BC's; here is an explanation on how I integrate different BC's:

I expose some public interfaces on each BC (similar to domain commands, - queries and - events), and have an intermediate layer in my infrastructure that translates this call to the other BC.

Here is an example interface for exposed commands in a BC:

public interface IHandleCommands
{
    void DoSomething(Guid SomeId,string SomeName);
}

I also have a similar one for exposed events

public interface IPublishEvents 
{
   void SomethingHappened(Guid SomeId,string SomeName);
}

Finally for my exposed data (i.e. the Queries in CQ(R)S) I have another interface, please note that this allows you to remove coupling between your domain model and query code at any given time.

public interface IQueryState
{
    IEnumerable<SomeData> ActiveData(DateTime From=DateTime.Minvalue, ... );
}

And my implementation looks like this:

public class SomeAR:IHandleCommands
{
    IPublishEvents Bus;

    public SomeAr(IPublishEvents Bus) 
    {
       this.Bus = Bus;
    }

    public void DoSomething(Guid x,string y)
    {
       Bus.SomethingHappened(SomeId: x,SomeName: y);
    }
}

After all, when you think about it: things like domain events can be done without the messaging as well; just replace the message classes by interface members, and replace the handlers by interface implementations that get injected into your BC.

These handlers then invoke commands on other BC's; they are like the glue that bind together different BC's (think workflows/stateless saga's etc).

This could be an example handler:

public class WorkFlow : IPublishEvents
{
  public void SomethingHappened(Guid SomeId,string SomeName) 
  {
     AnotherBC.DoSomething(SomeId,SomeName);
  }
}

This is an easy approach that does not require a lot of effort, and I have used this with great success. If you want to switch to full-blown messaging later on, it should be easy to do.

To answer your questions about the UI:

I think you are being too rigid about this.

As long as my domain is (or can be easily) decoupled from your UI, you can easily start with a single UI project, and then split it up the minute you start experiencing pain somewhere. However, if you do split up code, you should split it up per BC, so project structures match.

I find building a UI this way to be the most efficient way for me...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top