Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Microservices Platform & Commands Solution

A .NET 9.0 microservices solution demonstrating inter-service communication patterns including synchronous (HTTP REST, gRPC) and asynchronous (RabbitMQ) messaging.

πŸ“‹ Table of Contents

🎯 Overview

This solution consists of two microservices that work together to manage platforms and their associated commands:

  • PlatformService: Manages platform entities (Name, Cost, Publisher)
  • CommandsService: Manages commands associated with platforms (HowTo, CommandLine)

The services communicate using multiple patterns:

  • Synchronous: HTTP REST API and gRPC
  • Asynchronous: RabbitMQ Message Bus

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PlatformService │────────▢│ CommandsService β”‚
β”‚                 β”‚  HTTP   β”‚                 β”‚
β”‚                 β”‚  gRPC   β”‚                 β”‚
β”‚                 β”‚         β”‚                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                           β”‚
         β”‚                           β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
              β”‚  RabbitMQ   β”‚
              β”‚  MessageBus β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

  1. Platform Creation: When a platform is created in PlatformService:

    • Synchronous HTTP call to CommandsService (fallback)
    • Asynchronous message published to RabbitMQ
    • CommandsService receives message and creates platform locally
  2. Platform Synchronization: CommandsService can fetch platforms via:

    • gRPC call to PlatformService (preferred)
    • HTTP REST API call (fallback)

πŸ”§ Services

PlatformService

Purpose: Manages platform entities

Features:

  • CRUD operations for platforms
  • SQL Server database (Production) / InMemory database (Development)
  • Publishes platform events to RabbitMQ
  • Exposes gRPC endpoint for platform data
  • HTTP REST API endpoints

Ports:

  • HTTP API: 5279 (Development)
  • gRPC: 7196 (Development)

Database:

  • Development: InMemory Database
  • Production: SQL Server

CommandsService

Purpose: Manages commands associated with platforms

Features:

  • CRUD operations for commands
  • InMemory database
  • Subscribes to RabbitMQ events for platform updates
  • Consumes gRPC from PlatformService
  • HTTP REST API endpoints

Ports:

  • HTTP API: 6000 (Development)

Database:

  • InMemory Database

πŸ› οΈ Technology Stack

  • .NET 9.0 - Framework
  • ASP.NET Core - Web framework
  • Entity Framework Core 9.0.9 - ORM
  • SQL Server - Production database
  • RabbitMQ - Message broker
  • gRPC - High-performance RPC framework
  • AutoMapper - Object mapping
  • Docker - Containerization
  • Kubernetes - Orchestration
  • OpenAPI/Swagger - API documentation

πŸ“‘ Communication Patterns

1. Synchronous Communication

HTTP REST API

  • PlatformService β†’ CommandsService: HTTP POST when creating platforms
  • Standard REST endpoints for CRUD operations

gRPC

  • CommandsService β†’ PlatformService: gRPC call to fetch all platforms
  • Protocol Buffers definition: Protos/platforms.proto
  • More efficient than HTTP for inter-service communication

2. Asynchronous Communication

RabbitMQ Message Bus

  • Exchange: trigger (Fanout type)
  • Event Type: Platform_Published
  • PlatformService publishes platform creation events
  • CommandsService subscribes and processes events asynchronously

πŸ“¦ Prerequisites

To run the two services locally you need:

SQL Server is not needed locally: in Development both services use an in-memory database.

To deploy to Kubernetes you additionally need:

πŸš€ Getting Started

Run locally

The start order matters: RabbitMQ, then PlatformService, then CommandsService. CommandsService calls PlatformService over gRPC and connects to RabbitMQ while it starts, and it exits if either is missing.

1. Clone the repository

git clone https://github.com/ramez00/microservices-platform-commands.git
cd microservices-platform-commands

2. Start RabbitMQ

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

Skip this step if RabbitMQ is already installed and running on your machine. The management UI is at http://localhost:15672 (user guest, password guest).

3. Start PlatformService

Open a terminal in the repository root:

dotnet run --project PlatformService

Wait for these lines:

--> Using InMem Db
--> Seeding Data.....
Now listening on: http://localhost:7196
Now listening on: http://localhost:5279
Endpoint Address
REST API http://localhost:5279
gRPC http://localhost:7196

4. Start CommandsService

Open a second terminal in the repository root:

dotnet run --project CommandsService

Wait for these lines:

--> Calling GRPC Server http://localhost:7196
--> Seeding new platforms...
==> Connected to RabbitMQ and listening for messages.
Now listening on: http://localhost:6000
Endpoint Address
REST API http://localhost:6000

5. Check that it works

Follow the steps in Testing to create a platform in PlatformService and watch it arrive in CommandsService.

Stopping

Press Ctrl+C in each terminal, then stop the broker with docker stop rabbitmq (start it again later with docker start rabbitmq). All data is in memory, so every restart begins with the four seeded platforms.

Troubleshooting

Symptom Cause and fix
CommandsService exits at startup with a NullReferenceException PlatformService or RabbitMQ is not running. Start them first, then start CommandsService again.
PlatformService console shows Could not connect to the Message Bus and requests return 500 RabbitMQ is not reachable on localhost:5672. Start it and restart PlatformService.
http://localhost:6000 does not open in the browser (ERR_UNSAFE_PORT) Chrome, Edge and Firefox block port 6000. Use PowerShell, curl or Postman instead.
address already in use Another process holds port 5279, 7196 or 6000. Stop it, or change the port in appsettings.json (PlatformService) or Properties/launchSettings.json (CommandsService). If you change the gRPC port, update GrpcPlatform in CommandsService too.
A created platform never shows up in CommandsService CommandsService was not running when the event was published. Restart it and it reloads all platforms over gRPC.

Build the Docker images

docker build -t <your-dockerhub-user>/platformservice PlatformService
docker build -t <your-dockerhub-user>/commandservice CommandsService
docker push <your-dockerhub-user>/platformservice
docker push <your-dockerhub-user>/commandservice

Containers run in the Production environment, where the services look for SQL Server, RabbitMQ and each other under their Kubernetes service names (see appsettings.Production.json). The images are therefore meant for the Kubernetes deployment below and do not work with a plain docker run.

βš™οΈ Configuration

PlatformService Configuration

appsettings.json:

{
  "CommandService": "http://localhost:6000/api/c/Platforms/testPost",
  "ConnectionStrings": {
    "PlatformsConn": "Server=localhost,14333;Database=PlatformDb;User Id=sa;Password=P@ssw0rd!;TrustServerCertificate=True"
  },
  "RabbitMQHost": "localhost",
  "RabbitMQPort": "5672",
  "Kestrel": {
    "Endpoints": {
      "Grpc": {
        "Protocols": "Http2",
        "Url": "http://localhost:7196"
      },
      "webApi": {
        "Protocols": "Http1",
        "Url": "http://localhost:5279"
      }
    }
  }
}

CommandsService Configuration

appsettings.json:

{
  "RabbitMQHost": "localhost",
  "RabbitMQPort": "5672",
  "GrpcPlatform": "http://localhost:7196"
}

πŸ“š API Endpoints

PlatformService

GET /api/Platform/testApi

Test endpoint to verify service is running.

GET /api/Platform

Get all platforms.

GET /api/Platform/{id}

Get platform by ID.

POST /api/Platform/AddNewPlatform

Create a new platform (without event publishing).

Request Body:

{
  "name": "Platform Name",
  "cost": "Free",
  "publisher": "Publisher Name"
}

POST /api/Platform/CreatePlatForm

Create a new platform with event publishing (sends sync HTTP + async RabbitMQ).

Request Body:

{
  "name": "Platform Name",
  "cost": "Free",
  "publisher": "Publisher Name"
}

CommandsService

GET /api/c/Platforms

Test endpoint.

GET /api/c/Platforms/platforms

Get all platforms from CommandsService database.

GET /api/c/platforms/{platformId}/Commands

Get all commands for a specific platform.

GET /api/c/platforms/{platformId}/Commands/{commandId}

Get a specific command for a platform.

POST /api/c/platforms/{platformId}/Commands

Create a new command for a platform.

Request Body:

{
  "howTo": "How to do something",
  "commandLine": "command --option value"
}

🚒 Deployment

Kubernetes Deployment

The manifests are in the K8S directory. Apply them in this order from the repository root.

  1. Create the SQL Server password secret

    The SQL Server deployment reads its sa password from a secret named mssql. It must match the password in the PlatformsConn connection string in PlatformService/appsettings.Production.json.

    kubectl create secret generic mssql --from-literal=SA_PASSWORD="P@ssw0rd!"
  2. Deploy SQL Server

    kubectl apply -f K8S/local-pvc.yaml
    kubectl apply -f K8S/mssql-plat-depl.yaml
  3. Deploy RabbitMQ

    kubectl apply -f K8S/rabbitmq-depl.yaml
  4. Deploy PlatformService

    kubectl apply -f K8S/platforms-deply.yaml
  5. Deploy CommandsService

    kubectl apply -f K8S/commands-deply.yaml
  6. Deploy the Ingress (optional, needs the NGINX Ingress Controller)

    kubectl apply -f K8S/ingress-srv.yaml

    The ingress routes the host acme.com. Add this line to your hosts file (C:\Windows\System32\drivers\etc\hosts on Windows, /etc/hosts on Linux and macOS):

    127.0.0.1 acme.com
    

    The APIs are then available at http://acme.com/api/platform and http://acme.com/api/c/platforms.

  7. Check the result

    kubectl get pods
    kubectl get services
    kubectl logs deployment/platforms-depl
    kubectl logs deployment/commands-depl

    All pods should reach Running. If CommandsService started before PlatformService or RabbitMQ were ready, restart it:

    kubectl rollout restart deployment commands-depl

Docker Images

The deployments pull pre-built images from Docker Hub:

  • ramez007/platformservice:latest
  • ramez007/commandservice:latest

To deploy your own code changes, build and push the images under your own Docker Hub user (see Build the Docker images), change the image: lines in K8S/platforms-deply.yaml and K8S/commands-deply.yaml, and apply the manifests again.

πŸ“ Project Structure

Soluation/
β”œβ”€β”€ PlatformService/
β”‚   β”œβ”€β”€ Controllers/
β”‚   β”‚   └── PlatformController.cs
β”‚   β”œβ”€β”€ Data/
β”‚   β”‚   β”œβ”€β”€ ApplicationDbContext.cs
β”‚   β”‚   β”œβ”€β”€ IPlatformRepo.cs
β”‚   β”‚   β”œβ”€β”€ PlatformRepo.cs
β”‚   β”‚   └── PrepDb.cs
β”‚   β”œβ”€β”€ Models/
β”‚   β”‚   └── Platform.cs
β”‚   β”œβ”€β”€ Dtos/
β”‚   β”‚   β”œβ”€β”€ PlatformDto.cs
β”‚   β”‚   └── PlatformPublishedDto.cs
β”‚   β”œβ”€β”€ AsyncDataServices/
β”‚   β”‚   β”œβ”€β”€ IMessageBusClient.cs
β”‚   β”‚   └── MessageBusClient.cs
β”‚   β”œβ”€β”€ SyncDataServices/
β”‚   β”‚   β”œβ”€β”€ Http/
β”‚   β”‚   β”‚   β”œβ”€β”€ HttpCommandDataClient.cs
β”‚   β”‚   β”‚   └── ICommandDataClient.cs
β”‚   β”‚   └── Grpc/
β”‚   β”‚       └── GrpcPlatformService.cs
β”‚   β”œβ”€β”€ Protos/
β”‚   β”‚   └── platforms.proto
β”‚   β”œβ”€β”€ Mapping/
β”‚   β”‚   └── ProfileMapping.cs
β”‚   β”œβ”€β”€ Migrations/
β”‚   β”œβ”€β”€ Program.cs
β”‚   β”œβ”€β”€ Dockerfile
β”‚   └── PlatformService.csproj
β”‚
β”œβ”€β”€ CommandsService/
β”‚   β”œβ”€β”€ Controllers/
β”‚   β”‚   β”œβ”€β”€ CommandsController.cs
β”‚   β”‚   └── PlatformsController.cs
β”‚   β”œβ”€β”€ Data/
β”‚   β”‚   β”œβ”€β”€ AppDbContext.cs
β”‚   β”‚   β”œβ”€β”€ ICommandRepo.cs
β”‚   β”‚   β”œβ”€β”€ CommandRepo.cs
β”‚   β”‚   └── PrepDb.cs
β”‚   β”œβ”€β”€ Models/
β”‚   β”‚   β”œβ”€β”€ Command.cs
β”‚   β”‚   └── Platform.cs
β”‚   β”œβ”€β”€ Dtos/
β”‚   β”‚   β”œβ”€β”€ CommandDtos.cs
β”‚   β”‚   β”œβ”€β”€ PlatformDto.cs
β”‚   β”‚   β”œβ”€β”€ PlatformPublishedDto.cs
β”‚   β”‚   └── GenericEventDto.cs
β”‚   β”œβ”€β”€ AsyncDataServices/
β”‚   β”‚   └── MessageBusSubscriber.cs
β”‚   β”œβ”€β”€ SyncDataServices/
β”‚   β”‚   └── Grpc/
β”‚   β”‚       β”œβ”€β”€ IPlatformDataClient.cs
β”‚   β”‚       └── PlatformDataClient.cs
β”‚   β”œβ”€β”€ EventProcessing/
β”‚   β”‚   β”œβ”€β”€ IEventProcessor.cs
β”‚   β”‚   └── EventProcessor.cs
β”‚   β”œβ”€β”€ MapperProfiles/
β”‚   β”‚   └── CommandsProfile.cs
β”‚   β”œβ”€β”€ Protos/
β”‚   β”‚   └── platforms.proto
β”‚   β”œβ”€β”€ Program.cs
β”‚   β”œβ”€β”€ Dockerfile
β”‚   └── CommandsService.csproj
β”‚
└── K8S/
    β”œβ”€β”€ platforms-deply.yaml
    β”œβ”€β”€ commands-deply.yaml
    β”œβ”€β”€ rabbitmq-depl.yaml
    β”œβ”€β”€ mssql-plat-depl.yaml
    β”œβ”€β”€ ingress-srv.yaml
    └── local-pvc.yaml

πŸ” Key Components

Event Processing (CommandsService)

The EventProcessor class handles incoming RabbitMQ messages:

  • Detects event type from message
  • Processes Platform_Published events
  • Creates platform entries in CommandsService database
  • Prevents duplicate entries using ExternalID

Message Bus Client (PlatformService)

The MessageBusClient class:

  • Connects to RabbitMQ
  • Publishes platform creation events
  • Uses Fanout exchange for pub/sub pattern

gRPC Service (PlatformService)

The GrpcPlatformService class:

  • Implements gRPC service interface
  • Exposes GetAllPlatforms method
  • Returns platform data efficiently

gRPC Client (CommandsService)

The PlatformDataClient class:

  • Connects to PlatformService gRPC endpoint
  • Fetches all platforms
  • Maps gRPC models to domain models

πŸ§ͺ Testing

With both services running locally, these steps exercise all three communication paths. The commands are for PowerShell; the curl equivalents follow.

  1. List the platforms in PlatformService (four are seeded)

    Invoke-RestMethod http://localhost:5279/api/Platform
  2. List the platforms in CommandsService β€” the same four, copied over gRPC at startup

    Invoke-RestMethod http://localhost:6000/api/c/Platforms/platforms
  3. Create a platform β€” this publishes a Platform_Published event to RabbitMQ

    Invoke-RestMethod -Method Post http://localhost:5279/api/Platform/CreatePlatForm `
      -ContentType 'application/json' `
      -Body '{"name":"Kubernetes","cost":"Free","publisher":"CNCF"}'

    The CommandsService console shows:

    --> Message received from RabbitMQ.
    --> Platform Published Event Detected
    --> Platform added!
    
  4. List the platforms in CommandsService again β€” the new platform is now there

    Invoke-RestMethod http://localhost:6000/api/c/Platforms/platforms
  5. Add a command to a platform and read it back

    Invoke-RestMethod -Method Post http://localhost:6000/api/c/platforms/1/Commands `
      -ContentType 'application/json' `
      -Body '{"howTo":"Build a project","commandLine":"dotnet build"}'
    
    Invoke-RestMethod http://localhost:6000/api/c/platforms/1/Commands

curl equivalents

In Windows PowerShell use curl.exe, because curl is an alias for Invoke-WebRequest there.

curl http://localhost:5279/api/Platform
curl http://localhost:6000/api/c/Platforms/platforms

curl -X POST http://localhost:5279/api/Platform/CreatePlatForm \
  -H "Content-Type: application/json" \
  -d '{"name":"Kubernetes","cost":"Free","publisher":"CNCF"}'

curl -X POST http://localhost:6000/api/c/platforms/1/Commands \
  -H "Content-Type: application/json" \
  -d '{"howTo":"Build a project","commandLine":"dotnet build"}'

curl http://localhost:6000/api/c/platforms/1/Commands

πŸ“ Notes

  • Development Mode: Both services use InMemory databases for quick development
  • Production Mode: PlatformService switches to SQL Server
  • Event-Driven: CommandsService automatically receives platform updates via RabbitMQ
  • Fallback Mechanism: If RabbitMQ fails, HTTP sync call is attempted
  • gRPC: More efficient than HTTP for inter-service communication

πŸ” Security Considerations

  • Update default passwords in production
  • Use Kubernetes secrets for sensitive configuration
  • Implement authentication/authorization for production
  • Use HTTPS/TLS for production deployments
  • Secure RabbitMQ with authentication

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages