Custom Service Registry

Custom implementation of multi-tenant service registry and dynamic service discovery with PPM.Ocelot package. Technologies are .NET 8.0, Mongo DB, Vertical Slice Architecture, REPR Pattern, CQRS Pattern, Resilience Patterns (retry mechanisms and fallback strategy), Active health checks and auto deregister the unhealthy services, streaming logs via SSE, NGINX for deployment, active-passive failover and weighted round-robin load balancing.

Getting Started

1. Create a new tenant for your application

Request (POST)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/Tenant/SaveTenant' \
--header 'Content-Type: application/json' \
--data '{
    "ApplicationName": "Nyun Yat"
}'
    

Response

cURL
    
{
    "StatusCode": 200,
    "IsSuccess": true,
    "Message": "Success",
    "Data": 
    {
        "ApiKey": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747"
    }
}
    

2. Get Tenant Info

Request (GET)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/Tenant/GetTenantInfoById' \
--header 'X-Key: f3a9ab2e-8ba4-456f-b5dc-faced40ff747'
    

Response

cURL
    
{
    "StatusCode": 200,
    "IsSuccess": true,
    "Message": "Success",
    "Data": 
    {
        "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
        "ApplicationName": "Nyun Yat"
    }
}
    

3. Register Service

Request (POST)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/ServiceRegistry/RegisterService' \
--header 'X-Key: f3a9ab2e-8ba4-456f-b5dc-faced40ff747' \
--header 'Content-Type: application/json' \
--data '{
"ServiceName": "blog",
"Scheme": "http",
"HostName": "servicediscovery.somee.com",
"Port": 80,
"HealthCheckUrl": "http://servicediscovery.somee.com/health"
}'
    

Response (200)

cURL
    
{
    "StatusCode": 200,
    "IsSuccess": true,
    "Message": "Success",
    "Data": null
}
    

Response (400)

cURL
    
{
    "StatusCode": 400,
    "IsSuccess": false,
    "Message": "Service Name already exists.",
    "Data": null
}
    

4. Deregister Service

Request (POST)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/ServiceRegistry/DeregisterService' \
--header 'X-Key: f3a9ab2e-8ba4-456f-b5dc-faced40ff747' \
--header 'Content-Type: application/json' \
--data '{
"ServiceId": "77b3e250-24cb-4ca4-8b11-c64a23a4b24f"
}'
    

Response (200)

cURL
    
{
    "StatusCode": 200,
    "IsSuccess": true,
    "Message": "Success",
    "Data": null
}
    

Response (404)

cURL
    
{
    "StatusCode": 400,
    "IsSuccess": false,
    "Message": "Service not found.",
    "Data": null
}
    

5. Service Discovery

Request (GET)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/ServiceDiscovery/DiscoverService?serviceName=blog' \
--header 'X-Key: f3a9ab2e-8ba4-456f-b5dc-faced40ff747'
    

Response (200)

cURL
    
{
    "StatusCode": 200,
    "IsSuccess": true,
    "Message": "Success",
    "Data": 
        {
            "Services": [
                {
                    "ServiceId": "77b3e250-24cb-4ca4-8b11-c64a23a4b24f",
                    "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
                    "ServiceName": "blog",
                    "Scheme": "http",
                    "HostName": "servicediscovery.somee.com",
                    "Port": 80,
                    "HealthCheckUrl": "http://servicediscovery.somee.com/health"
                }
            ]
        }
}
    

Response (404)

cURL
    
{
    "StatusCode": 404,
    "IsSuccess": false,
    "Message": "Service not found.",
    "Data": null
}
    

6. Steaming logs real-time via SSE

Request (GET)

cURL
    
curl --location 'http://123.253.20.225:81/api/v1/ServiceLog/StreamLogs?apikey=f3a9ab2e-8ba4-456f-b5dc-faced40ff747'
    

Response

cURL
    
[
    {
        "Id": "67f878505756b5934a9dbdcd",
        "LogId": "511223e6-450f-4321-a719-3a0d2f4bcb23",
        "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
        "LogAt": "2025-04-11T02:02:56.625Z",
        "IsSuccess": true,
        "ErrorMessage": null,
        "ServiceInfo": 
        {
            "Id": "67f73cd6c8620e99821af216",
            "ServiceId": "6eaffd55-a173-467a-a444-b679a18750fa",
            "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
            "ServiceName": "blog",
            "Scheme": "http",
            "HostName": "servicediscovery.somee.com",
            "Port": 80,
            "HealthCheckUrl": "http://servicediscovery.somee.com/health"
        }
    },
    {
        "Id": "67f87850037d1462c8fef45c",
        "LogId": "9899185a-88cb-43f7-9b07-d7f1e0af267b",
        "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
        "LogAt": "2025-04-11T02:02:56.709Z",
        "IsSuccess": true,
        "ErrorMessage": null,
        "ServiceInfo": 
        {
            "Id": "67f73cd6c8620e99821af216",
            "ServiceId": "6eaffd55-a173-467a-a444-b679a18750fa",
            "TenantId": "f3a9ab2e-8ba4-456f-b5dc-faced40ff747",
            "ServiceName": "blog",
            "Scheme": "http",
            "HostName": "servicediscovery.somee.com",
            "Port": 80,
            "HealthCheckUrl": "http://servicediscovery.somee.com/health"
        }
    }
]