Microservices - Implementation Guide
Version: 1.0 Date: August 4, 2026 Purpose: Step-by-step implementation instructions and code templates
Table of Contents
- Phase 1: Setup & Foundation
- Phase 2: Micro-Frontend Extraction
- Phase 3: Core Services
- Phase 4: Business Services
- Phase 5: Final Services
- Code Templates
- Testing Strategies
- Deployment Guides
Phase 1: Setup & Foundation
Step 1.1: Directory Structure
Create the infrastructure directory structure:
mkdir -p services/{api-gateway,auth,user,org,product,order,analytics,settings,notify}
mkdir -p micro-frontends/{shell,auth,user,org,product,order,analytics,admin}
mkdir -p infrastructure/{docker,kubernetes,github,terraform}
mkdir -p shared/{http-client,validators,constants,types,utils}Step 1.2: Create Base Dockerfile
File: services/Dockerfile.base
FROM node:20-alpine
WORKDIR /app
# Install dependencies
COPY package*.json bun.lock* ./
RUN npm install -g bun && bun install --frozen-lockfile
# Copy source
COPY . .
# Build
RUN bun run build
# Expose
EXPOSE 3000
# Start
CMD ["bun", "start"]Step 1.3: Create API Gateway Service
File: services/api-gateway/src/index.ts
import express, { Express, Request, Response } from 'express';
import { createProxyMiddleware } from 'express-http-proxy';
import cors from 'cors';
import rateLimit from 'express-rate-limit';
import jwt from 'jsonwebtoken';
const app: Express = express();
const PORT = process.env.PORT || 3000;
// ─────────────────────────────────────────────────────────────
// Middleware Setup
// ─────────────────────────────────────────────────────────────
app.use(
cors({
origin: process.env.CORS_ORIGIN || ['http://localhost:3100', 'http://localhost:3101'],
credentials: true
})
);
app.use(express.json());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // 100 requests per windowMs
});
app.use(limiter);
// ─────────────────────────────────────────────────────────────
// Auth Middleware
// ─────────────────────────────────────────────────────────────
interface AuthRequest extends Request {
user?: {
id: string;
email: string;
role: string;
};
}
const authMiddleware = (req: AuthRequest, res: Response, next: Function) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return next(); // Public endpoint
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret');
req.user = decoded as any;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
app.use(authMiddleware);
// ─────────────────────────────────────────────────────────────
// Service Routes (Proxies)
// ─────────────────────────────────────────────────────────────
// Auth Service
app.use(
'/api/auth',
createProxyMiddleware({
target: process.env.AUTH_SERVICE_URL || 'http://localhost:3001',
changeOrigin: true,
pathRewrite: {
'^/api/auth': ''
},
onError: (err, req, res) => {
console.error('Auth Service Error:', err);
res.status(503).json({ error: 'Auth service unavailable' });
},
// Forward auth header to service
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// User Service
app.use(
'/api/users',
createProxyMiddleware({
target: process.env.USER_SERVICE_URL || 'http://localhost:3002',
changeOrigin: true,
pathRewrite: {
'^/api/users': ''
},
onError: (err, req, res) => {
console.error('User Service Error:', err);
res.status(503).json({ error: 'User service unavailable' });
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// Organization Service
app.use(
'/api/orgs',
createProxyMiddleware({
target: process.env.ORG_SERVICE_URL || 'http://localhost:3003',
changeOrigin: true,
pathRewrite: {
'^/api/orgs': ''
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// Product Service
app.use(
'/api/products',
createProxyMiddleware({
target: process.env.PRODUCT_SERVICE_URL || 'http://localhost:3004',
changeOrigin: true,
pathRewrite: {
'^/api/products': ''
}
})
);
// Order Service
app.use(
'/api/orders',
createProxyMiddleware({
target: process.env.ORDER_SERVICE_URL || 'http://localhost:3005',
changeOrigin: true,
pathRewrite: {
'^/api/orders': ''
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// Analytics Service
app.use(
'/api/analytics',
createProxyMiddleware({
target: process.env.ANALYTICS_SERVICE_URL || 'http://localhost:3006',
changeOrigin: true,
pathRewrite: {
'^/api/analytics': ''
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// Settings Service
app.use(
'/api/settings',
createProxyMiddleware({
target: process.env.SETTINGS_SERVICE_URL || 'http://localhost:3007',
changeOrigin: true,
pathRewrite: {
'^/api/settings': ''
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// Notification Service
app.use(
'/api/notify',
createProxyMiddleware({
target: process.env.NOTIFY_SERVICE_URL || 'http://localhost:3008',
changeOrigin: true,
pathRewrite: {
'^/api/notify': ''
},
onProxyReq: (proxyReq, req: AuthRequest) => {
if (req.headers.authorization) {
proxyReq.setHeader('Authorization', req.headers.authorization);
}
}
})
);
// ─────────────────────────────────────────────────────────────
// Health Checks
// ─────────────────────────────────────────────────────────────
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.get('/api/health', (req, res) => {
res.json({
gateway: 'ok',
timestamp: new Date().toISOString()
});
});
// ─────────────────────────────────────────────────────────────
// Start Server
// ─────────────────────────────────────────────────────────────
app.listen(PORT, () => {
console.log(`🚀 API Gateway running on port ${PORT}`);
console.log(` Auth Service: ${process.env.AUTH_SERVICE_URL}`);
console.log(` User Service: ${process.env.USER_SERVICE_URL}`);
console.log(` Product Service: ${process.env.PRODUCT_SERVICE_URL}`);
console.log(` Order Service: ${process.env.ORDER_SERVICE_URL}`);
});
export default app;Phase 2: Micro-Frontend Extraction
Step 2.1: Create Shell Application
File: micro-frontends/shell/src/app/layout.tsx
import React from 'react';
import { ReactNode } from 'react';
import { Suspense } from 'react';
export const metadata = {
title: 'Aori - Business Management Platform',
description: 'Centralized business management solution',
};
export default function RootLayout({
children,
}: {
children: ReactNode;
}) {
return (
<html lang="en">
<body>
<Suspense fallback={<div>Loading...</div>}>
<div id="root">
<Navigation />
<main>{children}</main>
<Footer />
</div>
</Suspense>
</body>
</html>
);
}
function Navigation() {
return (
<nav className="navbar">
<div className="logo">Aori</div>
<ul>
<li><a href="/products">Products</a></li>
<li><a href="/orders">Orders</a></li>
<li><a href="/account">Account</a></li>
<li><a href="/settings">Settings</a></li>
</ul>
</nav>
);
}
function Footer() {
return (
<footer>
<p>© 2026 Aori Labs. All rights reserved.</p>
</footer>
);
}Step 2.2: Module Federation Setup
File: micro-frontends/shell/next.config.ts
import { NextConfig } from 'next';
import { NextFederationPlugin } from '@module-federation/nextjs-mf';
const nextConfig: NextConfig = {
compiler: {
styledComponents: true
},
webpack: (config, options) => {
const { isServer } = options;
config.plugins.push(
new NextFederationPlugin({
name: 'shell',
filename: 'static/chunks/remoteEntry.js',
remotes: {
auth_module: !isServer
? new URL('http://localhost:3101/_next/static/chunks/remoteEntry.js', import.meta.url).href
: 'auth_module@http://auth-module:3000/_next/static/chunks/remoteEntry.js',
product_module: !isServer
? new URL('http://localhost:3104/_next/static/chunks/remoteEntry.js', import.meta.url).href
: 'product_module@http://product-module:3000/_next/static/chunks/remoteEntry.js',
order_module: !isServer
? new URL('http://localhost:3105/_next/static/chunks/remoteEntry.js', import.meta.url).href
: 'order_module@http://order-module:3000/_next/static/chunks/remoteEntry.js'
},
exposes: {
'./hooks': './src/hooks/index.ts',
'./context': './src/context/index.ts',
'./ui': './src/ui/index.ts'
},
shared: ['react', 'react-dom', 'zustand', 'swr']
})
);
return config;
}
};
export default nextConfig;Step 2.3: Module Loader Component
File: micro-frontends/shell/src/components/ModuleLoader.tsx
'use client';
import React, { Suspense, lazy, ComponentType } from 'react';
interface ModuleLoaderProps {
scope: string;
module: string;
fallback?: React.ReactNode;
}
export function ModuleLoader({
scope,
module,
fallback = <div>Loading module...</div>,
}: ModuleLoaderProps) {
const [Component, setComponent] = React.useState<ComponentType | null>(null);
const [error, setError] = React.useState<Error | null>(null);
React.useEffect(() => {
async function loadModule() {
try {
const container = await import(/* webpackIgnore: true */ `http://localhost:${getPortForScope(scope)}/_next/static/chunks/remoteEntry.js`);
await __webpack_share_scopes__.default.init(container.default);
const factory = await container.get(module);
const Module = factory().default;
setComponent(Module);
} catch (err) {
console.error(`Failed to load module ${module} from ${scope}:`, err);
setError(err as Error);
}
}
loadModule();
}, [scope, module]);
if (error) {
return <div>Error loading module: {error.message}</div>;
}
if (!Component) {
return fallback;
}
return <Component />;
}
function getPortForScope(scope: string): number {
const ports: Record<string, number> = {
auth_module: 3101,
product_module: 3104,
order_module: 3105,
user_module: 3102,
org_module: 3103,
analytics_module: 3106,
admin_module: 3107,
};
return ports[scope] || 3000;
}Phase 3: Core Services
Step 3.1: Auth Service Template
File: services/auth/src/index.ts
import express, { Express, Request, Response } from 'express';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';
const prisma = new PrismaClient();
const app: Express = express();
// ─────────────────────────────────────────────────────────────
// Middleware
// ─────────────────────────────────────────────────────────────
app.use(express.json());
// ─────────────────────────────────────────────────────────────
// Schemas
// ─────────────────────────────────────────────────────────────
const RegisterSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
const LoginSchema = z.object({
email: z.string().email(),
password: z.string()
});
// ─────────────────────────────────────────────────────────────
// Routes
// ─────────────────────────────────────────────────────────────
// Register
app.post('/register', async (req: Request, res: Response) => {
try {
const { email, password } = RegisterSchema.parse(req.body);
const existingUser = await prisma.user.findUnique({
where: { email }
});
if (existingUser) {
return res.status(409).json({ error: 'Email already exists' });
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.create({
data: {
email,
password: hashedPassword
}
});
const token = jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET || 'secret', { expiresIn: '7d' });
res.status(201).json({
user: { id: user.id, email: user.email },
token
});
} catch (error) {
res.status(400).json({ error: (error as Error).message });
}
});
// Login
app.post('/login', async (req: Request, res: Response) => {
try {
const { email, password } = LoginSchema.parse(req.body);
const user = await prisma.user.findUnique({
where: { email }
});
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET || 'secret', { expiresIn: '7d' });
res.json({
user: { id: user.id, email: user.email },
token
});
} catch (error) {
res.status(400).json({ error: (error as Error).message });
}
});
// Verify Token
app.get('/verify', (req: Request, res: Response) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret');
res.json({ valid: true, user: decoded });
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'healthy' });
});
// ─────────────────────────────────────────────────────────────
// Start
// ─────────────────────────────────────────────────────────────
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`🔐 Auth Service running on port ${PORT}`);
});Step 3.2: User Service Template
File: services/user/src/index.ts
import express, { Express, Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
import axios from 'axios';
const prisma = new PrismaClient();
const app: Express = express();
// ─────────────────────────────────────────────────────────────
// Middleware
// ─────────────────────────────────────────────────────────────
app.use(express.json());
// Auth verification middleware
const verifyToken = async (req: Request, res: Response, next: Function) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
try {
const response = await axios.get('http://auth:3001/verify', {
headers: { Authorization: `Bearer ${token}` }
});
(req as any).user = response.data.user;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// ─────────────────────────────────────────────────────────────
// Routes
// ─────────────────────────────────────────────────────────────
// Get user profile
app.get('/:id', verifyToken, async (req: Request, res: Response) => {
try {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
include: { profile: true }
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Update user profile
app.put('/:id', verifyToken, async (req: Request, res: Response) => {
try {
const user = await prisma.user.update({
where: { id: req.params.id },
data: req.body
});
res.json(user);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Delete user
app.delete('/:id', verifyToken, async (req: Request, res: Response) => {
try {
await prisma.user.delete({
where: { id: req.params.id }
});
res.json({ message: 'User deleted' });
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'healthy' });
});
// ─────────────────────────────────────────────────────────────
// Start
// ─────────────────────────────────────────────────────────────
const PORT = process.env.PORT || 3002;
app.listen(PORT, () => {
console.log(`👤 User Service running on port ${PORT}`);
});Phase 4: Business Services
Step 4.1: Product Service Template
File: services/product/src/index.ts
import express, { Express, Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
import Redis from 'ioredis';
const prisma = new PrismaClient();
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
const app: Express = express();
// ─────────────────────────────────────────────────────────────
// Routes
// ─────────────────────────────────────────────────────────────
// Get all products
app.get('/', async (req: Request, res: Response) => {
try {
// Check cache
const cached = await redis.get('products:all');
if (cached) {
return res.json(JSON.parse(cached));
}
const products = await prisma.product.findMany({
include: { category: true, images: true }
});
// Cache for 1 hour
await redis.setex('products:all', 3600, JSON.stringify(products));
res.json(products);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Get single product
app.get('/:id', async (req: Request, res: Response) => {
try {
const cached = await redis.get(`products:${req.params.id}`);
if (cached) {
return res.json(JSON.parse(cached));
}
const product = await prisma.product.findUnique({
where: { id: req.params.id },
include: { category: true, images: true }
});
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
// Cache for 1 hour
await redis.setex(`products:${req.params.id}`, 3600, JSON.stringify(product));
res.json(product);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Create product (admin only)
app.post('/', async (req: Request, res: Response) => {
try {
const product = await prisma.product.create({
data: req.body
});
// Invalidate cache
await redis.del('products:all');
res.status(201).json(product);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Update product
app.put('/:id', async (req: Request, res: Response) => {
try {
const product = await prisma.product.update({
where: { id: req.params.id },
data: req.body
});
// Invalidate cache
await redis.del('products:all');
await redis.del(`products:${req.params.id}`);
res.json(product);
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'healthy' });
});
// ─────────────────────────────────────────────────────────────
// Start
// ─────────────────────────────────────────────────────────────
const PORT = process.env.PORT || 3004;
app.listen(PORT, () => {
console.log(`📦 Product Service running on port ${PORT}`);
});Code Templates
Shared HTTP Client
File: shared/http-client/src/client.ts
import axios, { AxiosInstance, AxiosError } from 'axios';
export class HttpClient {
private instance: AxiosInstance;
constructor(baseURL: string, token?: string) {
this.instance = axios.create({
baseURL,
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` })
}
});
// Add response interceptor for error handling
this.instance.interceptors.response.use(
response => response,
(error: AxiosError) => {
if (error.response?.status === 401) {
// Handle token refresh
window.location.href = '/login';
}
return Promise.reject(error);
}
);
}
async get<T>(path: string, config?: any): Promise<T> {
const response = await this.instance.get<T>(path, config);
return response.data;
}
async post<T>(path: string, data?: any, config?: any): Promise<T> {
const response = await this.instance.post<T>(path, data, config);
return response.data;
}
async put<T>(path: string, data?: any, config?: any): Promise<T> {
const response = await this.instance.put<T>(path, data, config);
return response.data;
}
async delete<T>(path: string, config?: any): Promise<T> {
const response = await this.instance.delete<T>(path, config);
return response.data;
}
}Micro-Frontend Product Module Example
File: micro-frontends/product/src/app/page.tsx
'use client';
import { useEffect, useState } from 'react';
import useSWR from 'swr';
const fetcher = (url: string) =>
fetch(url).then((res) => res.json());
export default function ProductsPage() {
const { data: products, error, isLoading } = useSWR(
'/api/products',
fetcher
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading products</div>;
return (
<div className="products-grid">
{products?.map((product: any) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
function ProductCard({ product }: { product: any }) {
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price}</p>
<button>Add to Cart</button>
</div>
);
}Testing Strategies
Unit Tests
File: services/auth/__tests__/auth.test.ts
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import request from 'supertest';
import app from '../src/index';
describe('Auth Service', () => {
describe('POST /register', () => {
it('should register a new user', async () => {
const response = await request(app).post('/register').send({
email: 'test@example.com',
password: 'SecurePassword123'
});
expect(response.status).toBe(201);
expect(response.body.user).toHaveProperty('id');
expect(response.body).toHaveProperty('token');
});
it('should reject duplicate email', async () => {
// First registration
await request(app).post('/register').send({
email: 'duplicate@example.com',
password: 'SecurePassword123'
});
// Second registration with same email
const response = await request(app).post('/register').send({
email: 'duplicate@example.com',
password: 'AnotherPassword123'
});
expect(response.status).toBe(409);
});
});
describe('POST /login', () => {
it('should login with correct credentials', async () => {
// Register first
await request(app).post('/register').send({
email: 'login@example.com',
password: 'SecurePassword123'
});
// Login
const response = await request(app).post('/login').send({
email: 'login@example.com',
password: 'SecurePassword123'
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('token');
});
});
});Integration Tests
File: __tests__/integration/order-flow.test.ts
import { describe, it, expect } from '@jest/globals';
import axios from 'axios';
const API_GATEWAY = 'http://localhost:3000/api';
describe('Order Flow Integration', () => {
let authToken: string;
let userId: string;
let productId: string;
it('should complete full order flow', async () => {
// 1. Register user
const registerRes = await axios.post(`${API_GATEWAY}/auth/register`, {
email: 'test@example.com',
password: 'SecurePassword123'
});
authToken = registerRes.data.token;
userId = registerRes.data.user.id;
// 2. Fetch products
const productsRes = await axios.get(`${API_GATEWAY}/products`);
productId = productsRes.data[0].id;
// 3. Create order
const orderRes = await axios.post(
`${API_GATEWAY}/orders`,
{
userId,
items: [{ productId, quantity: 2 }]
},
{ headers: { Authorization: `Bearer ${authToken}` } }
);
expect(orderRes.status).toBe(201);
expect(orderRes.data.status).toBe('pending');
// 4. Check order history
const historyRes = await axios.get(`${API_GATEWAY}/users/${userId}/orders`, { headers: { Authorization: `Bearer ${authToken}` } });
expect(historyRes.data).toContainEqual(expect.objectContaining({ id: orderRes.data.id }));
});
});Deployment Guides
Local Development Setup
File: README_SETUP.md
# Local Development Setup
## Prerequisites
- Docker & Docker Compose
- Node.js 20+
- Bun 1.2.19+
## Quick Start
1. Clone repository:
\`\`\`bash
git clone https://github.com/ilkhoeri/aori-apps.git
cd aori-apps
\`\`\`
2. Start Docker services:
\`\`\`bash
docker-compose -f docker-compose.yml up -d
\`\`\`
3. Install dependencies:
\`\`\`bash
bun install
\`\`\`
4. Run development environment:
\`\`\`bash
bun dev
\`\`\`
5. Access applications:
- Shell: http://localhost:3100
- API Gateway: http://localhost:3000
- Auth Service: http://localhost:3001
- MongoDB: mongodb://root:password@localhost:27017
## Docker Compose Commands
\`\`\`bash
# Start all services
docker-compose up -d
# Stop all services
docker-compose down
# View logs
docker-compose logs -f service-name
# Restart specific service
docker-compose restart service-name
\`\`\`
## Service Ports
| Service | Port |
| -------------- | ----- |
| API Gateway | 3000 |
| Auth | 3001 |
| User | 3002 |
| Organization | 3003 |
| Product | 3004 |
| Order | 3005 |
| Analytics | 3006 |
| Settings | 3007 |
| Notification | 3008 |
| Shell (MFE) | 3100 |
| Auth Module | 3101 |
| Product Module | 3104 |
| Order Module | 3105 |
| MongoDB | 27017 |
| Redis | 6379 |Kubernetes Deployment
File: infrastructure/kubernetes/deployment-auth-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
labels:
app: auth-service
spec:
replicas: 2
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth-service
image: YOUR_REGISTRY/aori-auth:latest
ports:
- containerPort: 3001
env:
- name: NODE_ENV
value: 'production'
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: auth-db
key: connection-string
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: auth-secrets
key: jwt-secret
- name: REDIS_URL
value: 'redis://redis-service:6379'
livenessProbe:
httpGet:
path: /health
port: 3001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3001
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
memory: '256Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '500m'
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
spec:
selector:
app: auth-service
ports:
- protocol: TCP
port: 3001
targetPort: 3001
type: ClusterIPMonitoring & Observability
Health Check Setup
File: services/shared/health-check.ts
import express from 'express';
export function setupHealthChecks(app: express.Application) {
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
app.get('/readiness', async (req, res) => {
// Check database connection
try {
await prisma.$queryRaw`SELECT 1`;
res.json({ status: 'ready' });
} catch (error) {
res.status(503).json({ status: 'not ready' });
}
});
app.get('/metrics', (req, res) => {
// Prometheus metrics
res.set('Content-Type', 'text/plain');
res.send(`
# HELP request_total Total requests
# TYPE request_total counter
request_total{service="auth"} 1000
# HELP response_time_seconds Response time
# TYPE response_time_seconds histogram
response_time_seconds_bucket{service="auth",le="0.1"} 950
`);
});
}Success Metrics Checklist
Phase 1 Complete When:
- API Gateway routing all requests correctly
- All services responding on correct ports
- Docker Compose fully operational
- Local development setup documented
Phase 2 Complete When:
- Shell application running
- Module Federation working
- Modules loading dynamically
- Error boundaries functioning
Phase 3 Complete When:
- Auth Service fully operational
- User Service fully operational
- Organization Service fully operational
- Cross-service communication working
Phase 4 Complete When:
- Product Service operational
- Order Service operational
- Analytics Service operational
- Event bus messaging working
Phase 5 Complete When:
- All remaining services operational
- All micro-frontends running
- Monolithic API decommissioned
- Performance benchmarks met
Document Version: 1.0 Last Updated: August 4, 2026