This project implements a scalable and maintainable microservices architecture using NestJS, gRPC, and Azure Cosmos DB. The architecture is designed to promote modularity, scalability, and separation of concerns.
The project is organized into the following main directories:
apps/: Contains the API Gateway and individual microserviceslibs/: Shared libraries and core functionalityproto/: Protocol Buffer definitions for gRPC servicestools/: Code generators and development tools
The API Gateway (apps/api-gateway) serves as the entry point for client requests. It routes requests to the appropriate microservices and handles cross-cutting concerns such as authentication and request/response transformation.
Key principles:
- Modular Structure: The gateway is organized into modules (auth, user, order) for better separation of concerns.
- Configuration Management: Configurations for the app, gRPC clients, and Swagger are centralized in the
config/directory. - Proxy Pattern: Each module uses a proxy (e.g.,
user.proxy.ts) to communicate with its corresponding microservice via gRPC.
The libs/ directory contains shared code and core functionality used across multiple microservices.
The core library (libs/core) provides base classes and utilities for common tasks:
- Database Access:
BaseRepositoryindatabase/cosmos/base.repository.tsprovides a generic repository pattern for Cosmos DB operations. - Domain Models:
BaseEntityandValueObjectin thedomain/directory serve as base classes for domain entities and value objects. - gRPC Controllers:
BaseGrpcControlleringrpc/base.controller.tsprovides a foundation for gRPC method handlers.
The shared kernel (libs/shared-kernel) contains code shared across all services:
- DTOs: Data Transfer Objects for consistent data structures between services.
- Interfaces: Common interfaces, such as
IRepository. - Constants: Shared constant values, like error messages.
- Exceptions: Custom exception classes for standardized error handling.
The proto/ directory contains the Protocol Buffer definitions for gRPC services. These files define the contract between the API Gateway and the microservices.
The tools/ directory contains code generators to automate repetitive tasks:
- Proto Generator: Generates TypeScript interfaces from Protocol Buffer definitions.
- Service Generator: (To be implemented) Scaffolds new microservices.
- Separation of Concerns: Each microservice and module focuses on a specific domain or functionality.
- DRY (Don't Repeat Yourself): Common code is extracted into shared libraries to promote reusability.
- SOLID Principles: The architecture adheres to SOLID principles, particularly the Single Responsibility and Dependency Inversion principles.
- Scalability: The microservices architecture allows for independent scaling of services.
- Maintainability: Modular structure and clear separation of concerns make the codebase easier to maintain and extend.
- Type Safety: TypeScript is used throughout the project to ensure type safety and improve developer experience.
- Consistent Communication: gRPC is used for efficient and type-safe communication between services.
- Database Abstraction: The
BaseRepositoryprovides a consistent interface for database operations across services. - Error Handling: Centralized error messages and custom exceptions ensure consistent error reporting.
- Code Generation: Automated code generation tools improve developer productivity and maintain consistency.
-
Install dependencies:
pnpm install -
Generate TypeScript interfaces from Protocol Buffers:
pnpm run generate:proto -
Start the API Gateway:
pnpm run start:gateway -
Start individual microservices (implement as needed).
- Define new services or methods in the appropriate
.protofile. - Run the proto generator to create TypeScript interfaces.
- Implement the service logic in the corresponding microservice.
- Update the API Gateway to proxy requests to the new service.
- New Microservices: When creating a new microservice, follow the existing structure and use the shared libraries.
- Database Operations: Extend the
BaseRepositoryfor new entity types as needed. - Error Handling: Use the
ApplicationExceptionclass for custom errors and add new error messages to the shared constants. - DTOs: Define DTOs in the shared kernel for data structures used across multiple services.
- Testing: Write unit tests for individual components and integration tests for API endpoints.
By following these principles and best practices, you can maintain a scalable, maintainable, and efficient microservices architecture.