Skip to content

A practical example of how to build modular monoliths ("momo") with .NET 8

Notifications You must be signed in to change notification settings

CharlieDigital/dn8-modular-monolith

Repository files navigation

A Practical Guide to Modular Monoliths (MoMo) with .NET

This repo is inspired by a 2023 paper titled Towards Modern Development of Cloud Applications released by a team at Google.

Read Background

.NET’s host runtime model and built-in dependency injection makes building scalable “modular monoliths” easier than ever. This lets teams — especially startups — move faster with less development, deployment, and operational friction while still maintaining many of the benefits of microservices such as independent scaling of services, isolation of responsibilities, and so on.

⮑ Read more here: https://chrlschn.dev/blog/2024/01/a-practical-guide-to-modular-monoliths/

Running the Sample

💡 This sample repo is a very "naive" implementation that uses simple database level signaling. In a more robust system, we could use Postgres queues or an external service bus like SQS or Google Pub/Sub.

To run the sample in this repository, you'll need to have Docker installed.

There are two modes:

  1. Development mode. In this mode, we run all of the services in one single host
  2. Runtime mode. In this mode, we run each of the services in separate hosts

Development Mode

To run in development mode:

# Start the Postgres container
docker compose up

# API is at port 5228
cd src/core
dotnet run

In this mode, the Docker Compose file is responsible only for running our Postgres containers. We load all of our services into a single .NET host runtime which includes our REST API as well as our two background services which perform notification and updates to the Project.

Runtime Mode

To run in runtime mode:

# API is at port 8080
docker compose -f docker-compose-run.yaml up --build

In this mode, the Docker Compose file is responsible for building and running 4 containers:

  1. Postgres
  2. The .NET host for the API; we don't load the other services
  3. The .NET host for the service to notify WorkItem collaborator Users
  4. The .NET host for the service to check and update the Project for a WorkItem if all of the Project's WorkItems are completed.

Each container contains a host that loads a slice of the workload.

Testing

To test the example, use the following URLs to access the Swagger UI:

# In development mode
http://localhost:5228/swagger

# In runtime mode
http://localhost:8080/swagger

From here, follow these steps:

  1. Add a Project and get the ID from the response
  2. Add a User and keep the ID of the user
  3. Add a Work Item and link it to the Project and User
  4. The service WorkItemMonitorService will detect the new Work Item and simulate sending notifications to the collaborators
  5. Update the status of the Work Item to Completed
  6. The service WorkItemStatusMonitorService will detect changes in Work Item status and check the Project to see if all of the Work Items are completed and if so, update the status of the Project to Completed as well.

To execute this in curl:

# Add the project and note the response ID
curl -X 'POST' \
  'http://localhost:5228/api/projects/add' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '"Test"'

# Assume we get an ID of 8b90f42c-af9f-4c61-9b18-52e764f0ca8c

# Add a user:
curl -X 'POST' \
  'http://localhost:5228/api/users/add' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "User 1",
  "createdUtc": "2024-01-14T23:45:38.591Z",
  "email": "user1@example.com"
}'

# Now add a work item using the project ID and user ID:
curl -X 'POST' \
  'http://localhost:5228/api/tasks/add' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
  "projectId": "8b90f42c-af9f-4c61-9b18-52e764f0ca8c",
  "name": "Task 1",
  "collaborators": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ]
}'

# Assume we get an ID of: faba6123-183d-405d-8751-9736ed4a243b

# This will log:
# [NEW] Found new task Task 1 on project Test
#   ⮑  Notifying user User 1

# Finally, let's update the status with our work item ID:
curl -X 'POST' \
  'http://localhost:5228/api/tasks/faba6123-183d-405d-8751-9736ed4a243b/status' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '"Completed"'

Console output when a new task is detected:

Console output when a new task is detected

Console output when the task status is updated to Completed:

Console output when the task status is updated to Completed

About

A practical example of how to build modular monoliths ("momo") with .NET 8

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages