Splitting out the create dinner feature

In the DinnerController class the Create action is a relatively expensive database operation, which doesn't need to be synchronous. This feature is a good candidate for splitting into a separate component. I can publish a message from the web app instead of saving it to the database while the user waits—if the site is experiencing a high load, the message may wait in the queue for seconds or even minutes before being processed, but the response back to the user will be almost instant.

There are two pieces of work that need to be done to split the feature into a new component. The web application needs to publish a message to a queue when a dinner is created, and a message handler needs to listen on the queue and save the dinner when it receives a message. In NerdDinner, there's a bit more work to do because the existing code base is a physical monolith as well as a logical monolith—there's just one Visual Studio project that contains everything, all the model definitions as well as the UI code.

In this chapter's source code, I've added a new .NET assembly project called NerdDinner.Model to the solution and moved the EF classes to that project so that they can be shared between the web app and the message handler. The model project targets the full .NET Framework rather than the .NET Core, so I can use the existing code as it is and I don't need to bring an upgrade of EF into scope for this feature change. This choice restricts the message handler to being a full .NET Framework application too.

There's also a shared assembly project to isolate the message queue code in NerdDinner.Messaging. I'll be using the NATS message system, which is a high-performance open source message queue. There is a NATS client package on NuGet that targets .NET Standard, so it can be used in both .NET Framework and .NET Core, and my messaging project has the same client package. This means that I can be flexible so that other message handlers that don't use the EF model could be written in .NET Core.

In the model project, the original definition of the Dinner class is polluted with a lot of EF and MVC code to capture validation and storage behavior, like the following definition for the Description property:

[Required(ErrorMessage = "Description is required")]
[StringLength(256, ErrorMessage = "Description may not be longer than 256 characters")]
[DataType(DataType.MultilineText)]
public string Description { get; set; }

The class should be a simple POCO definition, but these attributes mean that the model definition is not portable because any consumers also need to reference EF and MVC. To avoid this in the messaging project, I have defined a simple Dinner entity without any of these attributes, and that class is the one I use to send dinner information in the messages. I can use the AutoMapper NuGet package to convert between Dinner class definitions, as the properties are fundamentally the same.

This is the sort of challenge you will find in lots of older projects—there's no clear separation of concerns, so breaking out features is not straightforward. You can take this approach to isolate shared components into new library projects. This is restructuring the code base without fundamentally changing its logic, which will help with modernizing the app.

The main code in the Create method of the DinnersController class now maps the dinner model to the clean Dinner entity and publishes an event instead of writing to the database:

if (ModelState.IsValid)
{
dinner.HostedBy = User.Identity.Name;
var eventMessage = new DinnerCreatedEvent
{
Dinner = Mapper.Map<entities.Dinner>(dinner),
CreatedAt = DateTime.UtcNow
};
MessageQueue.Publish(eventMessage);
return RedirectToAction("Index");
}

This is the fire-and-forget messaging pattern. The web application is the producer, publishing an event message. The producer doesn't wait for a response and doesn't know which components—if any—will consume the message and act on it. It's loosely coupled and fast, and it puts the responsibility to deliver the message on to the message queue, which is where it should be.

Listening for this event message is a new .NET Framework console project in NerdDinner.MessageHandlers.CreateDinner. The Main method of the console app uses the shared messaging project to open a connection to the message queue and subscribe to these dinner-created event messages. When a message is received, the handler maps the Dinner entity in the message back to a dinner model and saves the model to the database using code taken from the original implementation in the DinnersController class (and tidied up a little):

var dinner = Mapper.Map<models.Dinner>(eventMessage.Dinner);
using (var db = new NerdDinnerContext())
{
dinner.RSVPs = new List<RSVP>
{
new RSVP
{
AttendeeName = dinner.HostedBy
}
};
db.Dinners.Add(dinner);
db.SaveChanges();
}

Now the message handler can be packaged into its own Docker image and run in a container alongside the website container.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.38.92