| Build taxi-booking application :👈 | 👉:Service Per Business Capability Pattern |
Service-Per-Subdomain Pattern |
Service Per Subdomain Pattern in Microservices
Service Per Subdomain is a microservice design pattern where each microservice is built around a specific business subdomain from Domain-Driven Design (DDD).
Instead of creating services based on technical functions (such as UserService, DatabaseService, LoggingService), you split services according to business capabilities like:
- Customer Management
- Order Management
- Inventory Management
- Payment Processing
- Shipping
Each subdomain owns:
- Its business logic
- Its database
- Its APIs
- Its deployment
Why Service Per Subdomain?
In large applications, different business areas evolve independently.
For example, in an e-commerce system:
E-Commerce System
│
├── Customer Subdomain
├── Product Catalog Subdomain
├── Order Subdomain
├── Payment Subdomain
├── Shipping Subdomain
└── Notification Subdomain
Instead of one large monolith handling everything, each subdomain becomes a separate microservice.
Monolithic Approach
ECommerceApp
│
├── Customers
├── Products
├── Orders
├── Payments
├── Shipping
└── Notifications
Problems:
- Huge codebase
- Difficult deployments
- Tight coupling
- Slow development
- Scaling entire application for one heavy module
Service Per Subdomain Architecture
API Gateway
│
┌──────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
Product Service Order Service Customer Service
│ │ │
▼ ▼ ▼
ProductDB OrderDB CustomerDB
│
│
▼
Payment Service
│
▼
PaymentDB
Every service owns its data.
Example: Online Shopping Application
1. Customer Subdomain
Responsibilities:
- Registration
- Login
- Profile Management
- Address Management
Microservice:
CustomerService
Database:
CustomerDB
API:
GET /api/customers/1
POST /api/customers
2. Product Subdomain
Responsibilities:
- Product Catalog
- Categories
- Product Search
Microservice:
ProductService
Database:
ProductDB
Endpoints:
GET /api/products
GET /api/products/101
3. Order Subdomain
Responsibilities:
- Create Orders
- Track Orders
- Cancel Orders
Microservice:
OrderService
Database:
OrderDB
Endpoints:
POST /api/orders
GET /api/orders/5001
4. Payment Subdomain
Responsibilities:
- Payment Processing
- Refunds
- Payment Status
Microservice:
PaymentService
Database:
PaymentDB
Endpoints:
POST /api/payments
GET /api/payments/1001
.NET Example
Suppose we create an Order Service.
Structure
OrderService
│
├── Controllers
│ └── OrdersController.cs
│
├── Models
│ └── Order.cs
│
├── Data
│ └── OrderDbContext.cs
│
├── Repository
│ └── OrderRepository.cs
│
└── Program.cs
Order Model
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public decimal Amount { get; set; }
public DateTime OrderDate { get; set; }
}
DbContext
using Microsoft.EntityFrameworkCore;
public class OrderDbContext : DbContext
{
public OrderDbContext(DbContextOptions<OrderDbContext> options)
: base(options)
{
}
public DbSet<Order> Orders { get; set; }
}
Orders Controller
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly OrderDbContext _context;
public OrdersController(OrderDbContext context)
{
_context = context;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(int id)
{
var order = await _context.Orders.FindAsync(id);
if (order == null)
return NotFound();
return Ok(order);
}
[HttpPost]
public async Task<IActionResult> CreateOrder(Order order)
{
_context.Orders.Add(order);
await _context.SaveChangesAsync();
return Ok(order);
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<OrderDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("Default")));
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Service Communication Example
When creating an order:
Client
│
▼
Order Service
│
├── Validate Customer
│ │
│ ▼
│ Customer Service
│
├── Check Product
│ │
│ ▼
│ Product Service
│
└── Process Payment
│
▼
Payment Service
Synchronous Communication
Using HttpClient:
public class CustomerClient
{
private readonly HttpClient _httpClient;
public CustomerClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<CustomerDto> GetCustomerAsync(int id)
{
return await _httpClient
.GetFromJsonAsync<CustomerDto>(
$"api/customers/{id}");
}
}
Asynchronous Communication
Using RabbitMQ/Azure Service Bus.
Order Service publishes:
await _publishEndpoint.Publish(
new OrderCreated
{
OrderId = order.Id,
Amount = order.Amount
});
Payment Service listens:
public class OrderCreatedConsumer :
IConsumer<OrderCreated>
{
public async Task Consume(
ConsumeContext<OrderCreated> context)
{
var message = context.Message;
// Process payment
}
}
Database Per Service
A key rule in Service Per Subdomain:
✅ Good
Customer Service -> CustomerDB
Order Service -> OrderDB
Payment Service -> PaymentDB
❌ Bad
Customer Service -> SharedDB
Order Service -> SharedDB
Payment Service -> SharedDB
Why? Because shared databases create tight coupling and defeat the purpose of microservices.
Real-World Example: Amazon-like System
| Subdomain | Microservice |
|---|---|
| Customer Management | Customer Service |
| Product Catalog | Catalog Service |
| Shopping Cart | Cart Service |
| Order Processing | Order Service |
| Payments | Payment Service |
| Delivery | Shipping Service |
| Reviews | Review Service |
| Notifications | Notification Service |
Each service:
- Has its own database
- Can be deployed independently
- Can scale independently
- Can use different technologies if needed
Advantages
1. Independent Deployment
Deploy Order Service without touching Payment Service.
2. Independent Scaling
If order traffic increases:
Order Service = 10 instances
Payment Service = 2 instances
3. Better Ownership
Teams can own services:
Team A → Customer Service
Team B → Order Service
Team C → Payment Service
4. Fault Isolation
Payment failure does not crash Product Service.
Disadvantages
1. Distributed System Complexity
- Service discovery
- Network latency
- Retry handling
- Circuit breakers
2. Data Consistency
Transactions across services become difficult.
Example:
Order Created
Payment Failed
Inventory Reduced
Need Saga Pattern.
3. Monitoring
Need tools like:
- OpenTelemetry
- Seq
- ELK
- Application Insights
When to Use Service Per Subdomain
✅ Use when:
- Large enterprise applications
- Clear business domains
- Multiple development teams
- Independent deployments required
❌ Avoid when:
- Small applications
- Small teams
- Simple CRUD systems
- Startup MVPs
Summary
Service Per Subdomain means one microservice per business subdomain/bounded context. In a .NET microservices architecture, each service is typically an ASP.NET Core Web API with its own database, business logic, and deployment pipeline. This pattern aligns closely with Domain-Driven Design (DDD) and is one of the most recommended approaches for designing scalable enterprise microservices.
| Build taxi-booking application :👈 | 👉:Service Per Business Capability Pattern |
Comments and Discussion