Clean Architecture, SOLID, Repository pattern, Domain-Driven Design základy a jak navrhovat škálovatelné aplikace.
Každá vrstva závisí jen na vrstvách níže. Byznys logika nezávisí na frameworku, DB ani UI.
// Vrstvy (od centra ven):
// 1. Entities — core byznys objekty a pravidla
// 2. Use Cases — aplikační logika (co aplikace dělá)
// 3. Interface Adapters — controllers, presenters, gateways
// 4. Frameworks & Drivers — Express, React, Prisma, DB
// Dependency rule: závislosti VŽDY směrem dovnitř
// src/domain/entities/Product.ts (žádné závislosti!)
export class Product {
constructor(
public readonly id: number,
public readonly nazev: string,
private _cena: number,
) {}
get cena() { return this._cena; }
applyDiscount(pct: number): void {
if (pct < 0 || pct > 100) throw new Error('Invalid discount');
this._cena = this._cena * (1 - pct / 100);
}
}
// src/application/usecases/CreateProduct.ts
export class CreateProductUseCase {
constructor(private repo: IProductRepository) {} // interface!
async execute(dto: CreateProductDto): Promise<Product> {
const exists = await this.repo.findBySlug(dto.slug);
if (exists) throw new Error('Slug already taken');
const product = new Product(0, dto.nazev, dto.cena);
return this.repo.save(product);
}
}
// src/infrastructure/ProductRepository.ts (Prisma implementace)
export class PrismaProductRepository implements IProductRepository {
async save(product: Product) {
return prisma.product.create({ data: { ...product } });
}
}
constructor(private repo: IRepo) ne new PrismaRepo().// Repository — abstrakce nad datovou vrstvou
interface IUserRepository {
findById(id: number): Promise<User | null>;
findByEmail(email: string): Promise<User | null>;
save(user: User): Promise<User>;
delete(id: number): Promise<void>;
}
// Service — orchestrace use cases
class AuthService {
constructor(
private users: IUserRepository, // abstrakce!
private tokens: ITokenService,
private email: IEmailService,
) {}
async login(email: string, password: string) {
const user = await this.users.findByEmail(email);
if (!user || !await bcrypt.compare(password, user.password))
throw new UnauthorizedError();
return this.tokens.sign({ userId: user.id });
}
}
Docker, Docker Compose, Kubernetes základy, monitoring s Sentry a Datadog, logging a alerting.
# Multi-stage Dockerfile — malý produkční image
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
# Bezpečnost: spustit jako non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=deps --chown=appuser:appgroup /app/node_modules ./node_modules
EXPOSE 4000
CMD ["node", "dist/server.js"]
# docker-compose.prod.yml
version: '3.8'
services:
app:
build: .
ports: ["4000:4000"]
environment:
- DATABASE_URL=${DATABASE_URL}
- JWT_SECRET=${JWT_SECRET}
depends_on:
db: { condition: service_healthy }
restart: unless-stopped
db:
image: postgres:16-alpine
volumes: [db_data:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 5
nginx:
image: nginx:alpine
ports: ["80:80", "443:443"]
volumes: [./nginx.conf:/etc/nginx/nginx.conf]
volumes: { db_data: }
// Sentry — error tracking
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.1, // 10% transakcí
});
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.errorHandler()); // musí být za routes
// Structured logging (pino)
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: { level: (label) => ({ level: label }) },
});
// Log jako JSON pro log aggregátory (Datadog, Loki)
logger.info({ userId: 42, action: 'login', ip: req.ip }, 'User logged in');
logger.error({ err, orderId: order.id }, 'Order creation failed');
// Health check endpoint
app.get('/health', async (req, res) => {
const dbOk = await checkDb();
const status = dbOk ? 200 : 503;
res.status(status).json({
status: dbOk ? 'ok' : 'degraded',
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});
Kdy přejít na mikroservisy, API Gateway, message queues, event-driven architektura a distribuované transakce.
Začněte s monolitem. Přejděte na mikroservisy jen když máte konkrétní problém, který monolith nevyřeší.
// RabbitMQ / BullMQ pro asynchronní úlohy
import Queue from 'bull';
// Producer — přidat úlohu do fronty
const emailQueue = new Queue('emails', { redis: process.env.REDIS_URL });
await emailQueue.add('welcome', {
to: user.email,
subject: 'Vítejte v aplikaci',
userId: user.id,
}, {
attempts: 3, // 3 pokusy při selhání
backoff: { type: 'exponential', delay: 2000 },
});
// Consumer — zpracovat úlohy
emailQueue.process('welcome', async (job) => {
const { to, subject, userId } = job.data;
await sendEmail({ to, subject, template: 'welcome', userId });
await updateUserEmailSent(userId);
});
// Event-driven architektura
// Services komunikují přes events, ne přímými HTTP voláními
// OrderService → publish('order.created', order)
// EmailService → subscribe('order.created') → pošle potvrzení
// InventoryService → subscribe('order.created') → sníží sklad
// Výhoda: loose coupling, service neví o ostatních
Dokončili jste kompletní profi kurz. Certifikát, skill tree, odznaky a doporučené next steps pro vaši kariéru.
Kurz pokrývá technologie, které pracují v produkci u Airbnb, Vercel, Shopify a tisíců startupů. Teď záleží jen na praxi — stavte věci, open-source přispívejte, pište o tom.