User Interface
This post will conclude our story of a Vertical Slice of an application build to help First Pop Coffee Company be more successful with its Roast Planning. We are far from finished, as I know that the pain in actually implementing these architectural principles is stronger than I can describe with a single Bounded Context.
So how do we tie everything together for Nick to check out the Team’s prototype? We have a few dependencies that we need to wire up. We have those interfaces that we came up with in our Domain Model, the Bus and registering Command/Event Handlers, and the Read Model Facade that we just talked about.
For the UI, we’ll use the Ninject IoC container to wire up our dependencies. The Team would like to look at alternatives in the future… even the idea of not using an IoC container. For now, however, they will use Ninject.MVC5 to tap into ASP.NET MVC 5’s Composition Root to allow constructor injection into our MVC Controllers. Here’s what our Roast Planning Controller looks like:
public class RoastPlanningController : Controller
{
private readonly ICommandSender _bus;
private readonly IRoastPlanningReadModel _readModel;
public RoastPlanningController(ICommandSender bus, IRoastPlanningReadModel readModel)
{
_bus = bus;
_readModel = readModel;
}
public ActionResult Index()
{
var viewModel = _readModel.RoastSchedules.ToList();
return View(viewModel);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(string create)
{
_bus.Send(new CreateNewRoastScheduleCommand(Guid.NewGuid()));
return this.RedirectToAction(nameof(Index));
}
}
And here’s our Ninject Module that wires everything up via Ninject.MVC5:
public class RoastPlanningNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IEventStore>().To<EventStore>();
Bind<IRepository<RoastSchedule>>().To<EventSourcedRepository<RoastSchedule>>();
// Inject FakeDbSet<RoastScheduleView> to simulate persistence
Bind<FakeDbSet<RoastScheduleViewModel>>().ToSelf().InSingletonScope();
Bind<IRoastPlanningContext>().To<RoastPlanningContext>();
Bind<IRoastPlanningReadModel>().To<FakeRoastPlanningReadModel>();
Bind<IEventPublisher, ICommandSender>().To<FakeBus>()
.InSingletonScope()
.OnActivation(bus =>
{
bus.RegisterHandler<CreateNewRoastScheduleCommand>(
new RoastScheduleCommandHandlers(Kernel.Get<IRepository<RoastSchedule>>()).Handle);
bus.RegisterHandler<ChooseRoastDaysForRoastScheduleCommand>(
new RoastScheduleCommandHandlers(Kernel.Get<IRepository<RoastSchedule>>()).Handle);
bus.RegisterHandler<RoastScheduleCreatedEvent>(
new RoastScheduleView(Kernel.Get<IRoastPlanningContext>()).Handle);
});
}
}
What’s going on under the hood here?
- Index page loads with all of the
RoastScheduleViewModel
in the Read Model SQL Database - Nick navigates to /RoastPlanning/Create and sees a button to create a new Roast Schedule
- Nick clicks the “Create” button
- Command
CreateNewRoastScheduleCommand(Guid.NewGuid())
is sent to the Bus - Bus sends the Command to the Command Handler that’s registered for it
ICommandHandler<CreateNewRoastScheduleCommand>.Handle(CreateNewRoastScheduleCommand message)
is called- The Command is handled, which includes instantiating a new
RoastSchedule
and saving it to the Event Sourced Repository - Aggregate
RoastSchedule
queues upRoastScheduleCreatedEvent
to be flushed to the Event Store - Event Sourced Repository flushes
RoastScheduleCreatedEvent
to the Event Store viaSaveChanges
- Event Store emits
RoastScheduleCreatedEvent
to the Bus - Bus sends
RoastSchedleCreatedEvent
to handlers:IEventHandler<RoastScheduleCreatedEvent>.Handle(RoastScheduleCreatedEvent message)
that are registered RoastScheduleView.Handle(RoastScheduleCreatedEvent message)
is called to update the Read Model SQL Server viaRoastPlanningContext
RoastPlanningReadModel
now contains newestRoastScheduleViewModel
for the newly createdRoastSchedule
- Nick’s redirected to the Index page /RoastPlanning/Index and the newly created
RoastScheduleViewModel
is listed via the_readModel.RoastSchedules.ToList()
call
Here’s a demo gif:
A lot is being done just to accomplish this small task, however we’ve put the infrastructure in place to continue to model this Domain how it was discovered in the Design Level Event Storming:
Summary
This series has reached 25 mini-posts. This means I’d like to provide some closure so that we can step back and re-address some finer points about this style of software architecture.
We started with meeting Nick and First Pop Coffee for the first time as a Team of software developers that he wants to pay to help him develop software for his unique business idea: to offer roast-to-order coffee while allowing for him to allocate the amount of available roast batches through his online shop.
Nick and the Team went through a vigorous, yet effective process of Event Storming. They began with a Big Picture Event Storming session, which left them with an artifact that represented the overall idea that Nick has for using software to manage his coffee roasting business. They identified the major Subdomains in the business, and decided which ones were worth the application of Domain Driven Design.
They followed up the Big Picture Event Storming session up with a Design Level Event Storming Session. In this, they focused on the Roast Planning Bounded Context that they identified in the Big Picture session. They focused on Domain Driven Design terminology in this session, identifying Commands, Aggregates, Events, Read Models, etc in a way that Nick could understand how they fit in with his view of his domain.
The Team began working a prototype. They tried to implement a design using Domain Driven Design that applied patterns such as CQRS and Event Sourcing. They designed a couple Aggregates that were built with the ability to persist their state using an Event Store. They designed Command Handlers, Event Handlers, and a Bus that carried Messages throughout the Application.
Finally, they implemented a quick Read Model to be used by the User Interface for the Roast Planning Bounded Context. They built an in-memory version of what will eventually be a full-fledged persistence store that is designed to be optimized for reads. They IoC’d it all together quickly so to demonstrate the first Vertical Slice of the application for First Pop Coffee.
Our goal moving forward is to pick out smaller concepts and use First Pop Coffee as a foundation for exploring them. We’ll talk about aggregate design, process managers, and more production-worthy infrastructure projects that we can use for this type of project.
If you want to look at the full project on github, feel free to check it out here. It will change and it may not be organized perfectly, but if you want to build it, or even submit PRs I’m open!
I will be releasing most of this content only to subscribers. Make sure you sign up by clicking the big red button!
Related Posts:
- The Read Model Facade
- Processing Events on the Read Side
- Projection
- Publishing Events to the Bus
- Testing the Bus
- Sending Commands to the Bus
- The Bus
- The Read Model
- An Executable Specification
- Testing an Event Sourced Aggregate Root
- Aggregate Event Persistence
- Event Store
- Command Handlers
- Implementing an Event Sourced Aggregate
- Design Level Continued
- Complexity and Cost
- Design Level EventStorming
- People and Commands
- Hotspots
- Domain Discoveries
- Big Picture EventStorming
- The Domain - First Pop Coffee Company
- More Efficient Domain Modeling with EventStorming
- Where do you find resources for learning DDD, CQRS, Event Sourcing, and EventStorming?
- Has the code devolved into a big ball of mud?… What can you do about it?
- A Better Way to Project Domain Entities into DTOs
- Exposing IQueryable in a CQRS Query Stack
- A Pattern to Decouple your Aggregates from their Clients
- Erlang-style Supervisors in C# with Akka.NET and the Actor Model