This is just a Hosted Service that runs with a timer, this means the task is run at intervals. For example every 60 seconds poll a service for changes.
Hosted Service is a generic term Microsoft uses as they state “Hosted Services are services/logic that you host within your host/application/microservice.” The Microsoft project template for this is Worker Service (using the BackgroundService base class) and other common names I’ve seen are
Workers and my personal favorite
non-HTTP workloads like messaging, background tasks, console applications etc. These types of applications don’t have the notion of an incoming HTTP request like a traditional ASP.NET/ASP.NET Core Web Application - docs.microsoft.com
Consider the following events feed for a shopping cart below. These events are CRUD actions
CART_DELETE. The following query string parameters are used from the json-server instance.
- The watermark is set with
id_gte=42this means where
- Pagination is set with
_page=3this means only show page 3
- Set the page size with
_limit=25this means the page size is 25, ie: the max
eventscount will be 25
Create a hosted service that fulfills the following acceptance criteria:
- consumes this feed and persists the CRUD event to a database as a
watermarkneeds to be used so events are not processed again
- the service should run infinitely until the process is interupted with
- the solution should be extensible
This is the simplest no frills solution, from here it could be adapted to inlude message queues and better separation of concerns. The code for this solution can be found at https://github.com/carlpaton/TimedHostedServiceDemo
Create a timed background task per the Microsoft documentation using the IHostedService interface. Note that the BackgroundService is a base class for implementing a long running IHostedService. So this would work but I’ve use the interface instead as that the example for a time hosted service at docs.microsoft.com.
In Program.cs use
HostBuilderto configure the startup pipeline. Use
AddHostedService. Additionally add console logging with
ConfigureLoggingand configuration with
public class TimedHostedService : IHostedService, IDisposable
- Running the app now produces a timed event every 42 seconds and this is shown in the logs until
- Design a DDD-oriented microservice
- Timed background tasks
- Implement domain events
- Implement background tasks in microservices with IHostedService and the BackgroundService class
- Background tasks with hosted services in ASP.NET Core
- .NET Generic Host in ASP.NET Core
- Application Insights for Worker Service applications (non-HTTP applications)
This orchestrates the flow and doesnt have to be called a
Broker could be
Processor or anything that makes sense to your team. This will be called by DoWork in
TimedHostedService and only needs one method
- HttpEventFeedService implementation of IEventFeedService. I used the
Httpprefix as these events could be fetched by other implementations such as local config file.
The domain events are used to explicitly implement side effects across multiple aggregates. In C#, a domain event is simply a data-holding structure or class, like a DTO.
Our EventType‘s are based on the feed.
public enum EventType
To separate out the concearns we have handlers for each event type.
- CartCreateEventHandler implementation of IEventHandler.
- EmailOnCartCreateEventHandler implementation of ICartMapper (this is just an example of the extensibility, it doesnt actually send emails - but it could)
ICartRepository are very simliar.
- ICartRepository implementation of ICartRepository.
- CartItemRepository implementation of ICartItemRepository.
IWatermarkRepository is used to keep track of the watermark in a local .ini file.
At the start of each
ProcessAsync in the EventBroker the watermark is read from the ini file.
After each event is dispatched and awaited the watermark is updated with
+1. This implementation of the watermark would NOT scale well if the hosted service was with muiltiple instances but this is fine for a POC.
Thats it, the hosted service would continue to poll for new events and handle them. When new events are needed they can be implemented with IEventHandler and injected as part of the collection using dependency injection.