A comprehensive, enterprise-grade invoice management system built with modern microservices architecture and React design patterns, demonstrating advanced software engineering principles, scalable design patterns, and production-ready practices.
This application showcases Microservices Architecture combined with Feature-Sliced Design (FSD) on the frontend - a modern, scalable approach that promotes maintainability, testability, and team collaboration across distributed systems.
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Frontend β β API Gateway β β Microservices β
β React App βββββΊβ NestJS βββββΊβ Cluster β
β Port: 3001 β β Port: 5000 β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Message Broker β
β Apache Kafka β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Auth Service β β Invoice Service β β Product Service β
β Laravel + β β NestJS β β NestJS β
β NestJS β β PostgreSQL β β PostgreSQL β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
βNotification Svc β β Analytics Svc β β Utility Svc β
β NestJS β β NestJS β β PHP β
β Port: 3004 β β Elasticsearch β β Port: 3005 β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
appreact/src/
βββ app/ # Application Layer - Configuration & Setup
β βββ providers/ # React Providers (Redux, Router, i18n)
β βββ routing/ # Route configuration and guards
β βββ store/ # Redux store configuration
βββ pages/ # Pages Layer - Route-level components
β βββ LoginPage/ # Authentication page
β βββ DashboardPage/ # Main dashboard
β βββ InvoicesPage/ # Invoice listing
β βββ SettingsPage/ # Application settings
βββ features/ # Features Layer - Business logic modules
β βββ auth/ # Authentication & authorization
β β βββ ui/ # Auth UI components
β β βββ model/ # Auth business logic
β β βββ api/ # Auth API calls
β βββ invoice/ # Invoice management
β β βββ ui/ # Invoice forms, templates
β β βββ model/ # Invoice calculations, validation
β β βββ api/ # Invoice CRUD operations
β βββ product/ # Product catalog management
β βββ kontrahent/ # Client/contractor management
β βββ settings/ # Application configuration
βββ entities/ # Entities Layer - Business domain models
β βββ user/ # User entity and contexts
β βββ invoice/ # Invoice entity and business logic
β βββ product/ # Product entity management
β βββ kontrahent/ # Contractor entity management
βββ shared/ # Shared Layer - Reusable components & utilities
βββ ui/ # UI components library
βββ lib/ # Custom hooks and utilities
βββ api/ # API configuration and services
βββ utils/ # Helper functions and constants
- π Microservices Isolation: Each service is independently deployable and scalable
- ποΈ Layer Isolation: Frontend follows FSD with unidirectional dependencies
- π§© Service Autonomy: Services communicate via Kafka events
- π¦ API Gateway: Centralized routing and cross-cutting concerns
- π§ Horizontal Scaling: Individual services can be scaled based on demand
- π Technology Diversity: Best tool for each service (NestJS, PHP, Laravel)
Location: backend-nestjs/src/
@Controller('api')
export class ApiGatewayController {
constructor(
private readonly invoiceService: InvoiceProxyService,
private readonly productService: ProductProxyService,
private readonly authService: AuthProxyService,
) {}
@Get('invoices')
async getInvoices(@Headers() headers) {
return this.invoiceService.proxyRequest('/invoices', headers);
}
@Post('invoices')
async createInvoice(@Body() data, @Headers() headers) {
// Cross-cutting concerns: authentication, logging, rate limiting
await this.authService.validateToken(headers.authorization);
return this.invoiceService.proxyRequest('/invoices', headers, data);
}
}
Location: microservices/*/src/events/
@Injectable()
export class InvoiceEventHandler {
constructor(private readonly kafkaService: KafkaService) {}
async handleInvoiceCreated(invoice: Invoice) {
await this.kafkaService.emit('invoice.created', {
id: invoice.id,
amount: invoice.total,
customerId: invoice.customerId,
timestamp: new Date(),
});
}
@EventPattern('invoice.created')
async onInvoiceCreated(data: InvoiceCreatedEvent) {
// Analytics service listens and processes
await this.analyticsService.recordSale(data);
// Notification service sends confirmation email
await this.notificationService.sendInvoiceNotification(data);
}
}
Location: appreact/src/features/invoice/ui/InvoicesTemplates/factoryInvoicePrinter.js
const InvoiceTemplateFactory = {
templates: new Map([
['modern', ModernTemplate],
['corporate', CorporateTemplate],
['creative', CreativeTemplate],
['minimal', MinimalTemplate],
]),
create(templateType, props) {
const TemplateComponent = this.templates.get(templateType);
if (!TemplateComponent) {
throw new Error(`Unknown template type: ${templateType}`);
}
return <TemplateComponent {...props} />;
},
register(templateType, component) {
this.templates.set(templateType, component);
},
};
const FactoryInvoicePrinter = () => {
const selectedTemplate = useSelector(
(state) => state?.settings.settings?.templateInvoice || 'modern',
);
const templateProps = {
invoice: useSelector((state) => state.invoice.current),
company: useSelector((state) => state.company.info),
theme: useSelector((state) => state.settings.theme),
};
return InvoiceTemplateFactory.create(selectedTemplate, templateProps);
};
Benefits:
- β Encapsulates template creation logic
- β Runtime template registration
- β Consistent interface across templates
- β Easy A/B testing of templates
Location: appreact/src/entities/*/model/useContexts.js
// Service Container for Dependency Injection
class ServiceContainer {
constructor() {
this.services = new Map();
}
register(name, factory) {
this.services.set(name, factory);
}
resolve(name) {
const factory = this.services.get(name);
return factory ? factory() : null;
}
}
const container = new ServiceContainer();
// Register services
container.register('apiService', () => new ApiService());
container.register('cacheService', () => new CacheService());
// Invoice Context Provider with DI
export const InvoiceProvider = ({ children }) => {
const apiService = container.resolve('apiService');
const cacheService = container.resolve('cacheService');
const invoiceService = useMemo(
() => ({
async loadInvoices() {
const cached = await cacheService.get('invoices');
if (cached) return cached;
const data = await apiService.get('/invoices');
await cacheService.set('invoices', data, 300); // 5min cache
return data;
},
async createInvoice(invoiceData) {
const result = await apiService.post('/invoices', invoiceData);
await cacheService.invalidate('invoices');
return result;
},
}),
[apiService, cacheService],
);
return (
<InvoiceContext.Provider value={invoiceService}>
{children}
</InvoiceContext.Provider>
);
};
// Composition of providers
<ServiceProvider>
<InvoiceProvider>
<ProductProvider>
<KontrahentProvider>
<Application />
</KontrahentProvider>
</ProductProvider>
</InvoiceProvider>
</ServiceProvider>;
Location: appreact/src/app/store/
// Redux store with middleware
const store = configureStore({
reducer: {
auth: authReducer,
invoices: invoicesReducer,
products: productReducer,
notifications: notificationReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(sagaMiddleware)
.concat(websocketMiddleware)
.concat(analyticsMiddleware),
});
// Saga for side effects
function* watchInvoiceActions() {
yield takeEvery('invoices/create', function* (action) {
try {
// Optimistic update
yield put(invoicesActions.addOptimistic(action.payload));
// API call
const result = yield call(api.createInvoice, action.payload);
// Update with real data
yield put(invoicesActions.createSuccess(result));
// Side effects
yield put(
notificationsActions.show({
type: 'success',
message: 'Invoice created successfully',
}),
);
// Analytics tracking
yield call(analytics.track, 'invoice_created', {
amount: result.total,
currency: result.currency,
});
} catch (error) {
yield put(invoicesActions.createFailure(error));
yield put(invoicesActions.removeOptimistic(action.payload.tempId));
}
});
}
Location: appreact/src/features/invoice/model/commands.js
class Command {
execute() {
throw new Error('Must implement execute');
}
undo() {
throw new Error('Must implement undo');
}
}
class AddInvoiceItemCommand extends Command {
constructor(invoice, item) {
super();
this.invoice = invoice;
this.item = item;
}
execute() {
this.invoice.addItem(this.item);
return this.invoice;
}
undo() {
this.invoice.removeItem(this.item.id);
return this.invoice;
}
}
class InvoiceCommandManager {
constructor() {
this.history = [];
this.currentIndex = -1;
}
execute(command) {
// Remove any commands after current index
this.history = this.history.slice(0, this.currentIndex + 1);
// Execute and add to history
const result = command.execute();
this.history.push(command);
this.currentIndex++;
return result;
}
undo() {
if (this.canUndo()) {
const command = this.history[this.currentIndex];
this.currentIndex--;
return command.undo();
}
}
redo() {
if (this.canRedo()) {
this.currentIndex++;
const command = this.history[this.currentIndex];
return command.execute();
}
}
canUndo() {
return this.currentIndex >= 0;
}
canRedo() {
return this.currentIndex < this.history.length - 1;
}
}
// Usage in React component
const useInvoiceCommands = () => {
const commandManager = useRef(new InvoiceCommandManager());
const addItem = useCallback(
(item) => {
const command = new AddInvoiceItemCommand(invoice, item);
const newInvoice = commandManager.current.execute(command);
setInvoice(newInvoice);
},
[invoice],
);
const undo = useCallback(() => {
const newInvoice = commandManager.current.undo();
if (newInvoice) setInvoice(newInvoice);
}, []);
return { addItem, undo, canUndo: commandManager.current.canUndo() };
};
Location: appreact/src/shared/api/decorators.js
class CacheDecorator {
constructor(apiService, cacheService) {
this.apiService = apiService;
this.cacheService = cacheService;
}
async get(url, options = {}) {
const cacheKey = `api:${url}:${JSON.stringify(options)}`;
const cached = await this.cacheService.get(cacheKey);
if (cached && !options.fresh) {
return cached;
}
const result = await this.apiService.get(url, options);
await this.cacheService.set(cacheKey, result, options.ttl || 300);
return result;
}
async post(url, data, options = {}) {
const result = await this.apiService.post(url, data, options);
// Invalidate related cache entries
if (options.invalidateCache) {
await this.cacheService.invalidatePattern(options.invalidateCache);
}
return result;
}
}
class LoggingDecorator {
constructor(apiService) {
this.apiService = apiService;
}
async get(url, options = {}) {
console.log(`API GET: ${url}`, options);
const start = performance.now();
try {
const result = await this.apiService.get(url, options);
console.log(`API GET: ${url} - ${performance.now() - start}ms`);
return result;
} catch (error) {
console.error(`API GET ERROR: ${url}`, error);
throw error;
}
}
}
// API Service with decorators
const createApiService = () => {
let service = new BaseApiService();
service = new CacheDecorator(service, cacheService);
service = new LoggingDecorator(service);
return service;
};
// β Bad: Multiple responsibilities
const InvoiceComponent = ({ invoice }) => {
const [total, setTotal] = useState(0);
// Calculation logic
useEffect(() => {
const sum = invoice.items.reduce(
(acc, item) => acc + item.price * item.quantity * (1 + item.taxRate),
0,
);
setTotal(sum);
}, [invoice.items]);
// Validation logic
const validate = () => {
return invoice.items.length > 0 && invoice.customer;
};
// Rendering logic
return <div>{/* Complex JSX */}</div>;
};
// β
Good: Separated concerns
const useInvoiceCalculations = (items) => {
return useMemo(() => {
return items.reduce(
(acc, item) => acc + item.price * item.quantity * (1 + item.taxRate),
0,
);
}, [items]);
};
const useInvoiceValidation = (invoice) => {
return useMemo(
() => ({
isValid: invoice.items.length > 0 && invoice.customer,
errors: {
items: invoice.items.length === 0 ? 'At least one item required' : null,
customer: !invoice.customer ? 'Customer is required' : null,
},
}),
[invoice],
);
};
const InvoiceComponent = ({ invoice }) => {
const total = useInvoiceCalculations(invoice.items);
const validation = useInvoiceValidation(invoice);
return (
<InvoiceView invoice={invoice} total={total} validation={validation} />
);
};
// β
Abstract interfaces
interface IApiService {
get(url: string): Promise<any>;
post(url: string, data: any): Promise<any>;
}
interface ICacheService {
get(key: string): Promise<any>;
set(key: string, value: any, ttl?: number): Promise<void>;
}
// β
High-level modules depend on abstractions
class InvoiceService {
constructor(
private apiService: IApiService,
private cacheService: ICacheService
) {}
async getInvoices(): Promise<Invoice[]> {
const cached = await this.cacheService.get('invoices');
if (cached) return cached;
const invoices = await this.apiService.get('/invoices');
await this.cacheService.set('invoices', invoices, 300);
return invoices;
}
}
// β
Dependency injection in React
const InvoiceContainer = () => {
const apiService = useContext(ApiServiceContext);
const cacheService = useContext(CacheServiceContext);
const invoiceService = useMemo(
() => new InvoiceService(apiService, cacheService),
[apiService, cacheService]
);
return <InvoiceList service={invoiceService} />;
};
// β
Extensible without modification
abstract class PaymentProcessor {
abstract process(amount: number, details: any): Promise<PaymentResult>;
async processWithLogging(amount: number, details: any): Promise<PaymentResult> {
console.log(`Processing payment: ${amount}`);
const result = await this.process(amount, details);
console.log(`Payment result: ${result.success}`);
return result;
}
}
class StripePaymentProcessor extends PaymentProcessor {
async process(amount: number, details: any): Promise<PaymentResult> {
// Stripe-specific implementation
return await stripe.charges.create({ amount, ...details });
}
}
class PayPalPaymentProcessor extends PaymentProcessor {
async process(amount: number, details: any): Promise<PaymentResult> {
// PayPal-specific implementation
return await paypal.payment.create({ amount, ...details });
}
}
// Factory for payment processors
class PaymentProcessorFactory {
static create(type: string): PaymentProcessor {
switch(type) {
case 'stripe': return new StripePaymentProcessor();
case 'paypal': return new PayPalPaymentProcessor();
default: throw new Error(`Unknown payment processor: ${type}`);
}
}
}
// Event-driven architecture with Kafka
@Injectable()
export class InvoiceService {
constructor(
private readonly kafkaService: KafkaService,
private readonly repository: InvoiceRepository,
) {}
async createInvoice(data: CreateInvoiceDto): Promise<Invoice> {
const invoice = await this.repository.create(data);
// Publish event for other services
await this.kafkaService.emit('invoice.created', {
invoiceId: invoice.id,
customerId: invoice.customerId,
amount: invoice.total,
currency: invoice.currency,
timestamp: new Date(),
});
return invoice;
}
@EventPattern('payment.completed')
async handlePaymentCompleted(data: PaymentCompletedEvent) {
await this.repository.markAsPaid(data.invoiceId);
await this.kafkaService.emit('invoice.paid', {
invoiceId: data.invoiceId,
paymentId: data.paymentId,
timestamp: new Date(),
});
}
}
// Command side
@CommandHandler(CreateInvoiceCommand)
export class CreateInvoiceHandler {
constructor(private repository: InvoiceRepository) {}
async execute(command: CreateInvoiceCommand): Promise<void> {
const invoice = new Invoice(command.data);
await this.repository.save(invoice);
// Publish domain event
DomainEvents.raise(new InvoiceCreatedEvent(invoice.id));
}
}
// Query side
@QueryHandler(GetInvoicesQuery)
export class GetInvoicesHandler {
constructor(private readModel: InvoiceReadModel) {}
async execute(query: GetInvoicesQuery): Promise<InvoiceDto[]> {
return this.readModel.findInvoices(query.filters);
}
}
// Projection for read model
@EventsHandler(InvoiceCreatedEvent)
export class InvoiceProjection {
constructor(private readModel: InvoiceReadModel) {}
async handle(event: InvoiceCreatedEvent): Promise<void> {
await this.readModel.createProjection({
id: event.invoiceId,
// ... denormalized data for fast reads
});
}
}
class CircuitBreaker {
private failures = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private nextAttempt = Date.now();
constructor(
private readonly threshold = 5,
private readonly timeout = 60000,
) {}
async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
private onFailure() {
this.failures++;
if (this.failures >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
}
}
}
// Usage in service
@Injectable()
export class ProductService {
private circuitBreaker = new CircuitBreaker();
async getProducts(): Promise<Product[]> {
return this.circuitBreaker.call(async () => {
return this.httpService.get('/products').toPromise();
});
}
}
- React 18.3.1 - Concurrent features, Suspense, automatic batching
- TypeScript 5.0 - Advanced type system with template literals
- Redux Toolkit - RTK Query for data fetching
- Material-UI v5 - Emotion styling engine
- React Router v6 - Nested routing with data loading
- React Hook Form - Uncontrolled components for performance
- i18next - Interpolation and pluralization
- React Query - Server state synchronization
- NestJS 10 - Decorators, Guards, Interceptors, Pipes
- PHP 8.1 - For utility services
- Laravel 10 - Authentication service
- PostgreSQL 15 - JSONB, CTEs, Window functions
- MongoDB 6 - GridFS, Change streams
- Redis 7 - Streams, Modules, ACLs
- Elasticsearch 8 - Vector search, ML features
- Docker - Multi-stage builds, health checks
- Apache Kafka - Exactly-once semantics
- Nginx - Load balancing, SSL termination
- Prometheus - Metrics collection
- Grafana - Monitoring dashboards
- Docker Desktop - Latest version
- Node.js 18+ - With npm/yarn
- Docker Compose - v2.0+
# Clone the repository
git clone <repository-url>
cd InvoiceApp
# Start all microservices
./start-all-microservices.sh
# Start infrastructure
docker-compose up -d mongodb postgres redis elasticsearch kafka
# Backend services
cd backend-nestjs && npm run start:dev
cd microservices/auth-service && npm run start:dev
cd microservices/invoice-service && npm run start:dev
# Frontend (recommended: yarn for better performance)
cd appreact
yarn install
yarn start
- Frontend: http://localhost:3001
- API Gateway: http://localhost:5000
- Swagger Docs: http://localhost:5000/api-docs
- Kibana: http://localhost:5601
- Kafka UI: http://localhost:8080
- Auth Service: http://localhost:5000/api/auth/health
- Invoice Service: http://localhost:5000/api/invoices/health
- Product Service: http://localhost:3003/health
- Analytics: http://localhost:5000/api/analytics/health
- WebSocket connections for live invoice updates
- Server-Sent Events for notifications
- Optimistic UI updates with rollback
- React.memo for component memoization
- useMemo/useCallback for expensive calculations
- Code splitting with React.lazy()
- Service Worker for offline functionality
- JWT with refresh token rotation
- RBAC with granular permissions
- API rate limiting per user/IP
- Input sanitization and validation
# Frontend tests
cd appreact
yarn test # Unit tests
yarn test:integration # Integration tests
yarn test:e2e # End-to-end tests
# Backend tests
cd backend-nestjs
npm run test # Unit tests
npm run test:e2e # API tests
npm run test:load # Performance tests
# Build all services
docker-compose -f docker-compose.prod.yml build
# Deploy with health checks
docker-compose -f docker-compose.prod.yml up -d
# Monitor deployment
docker-compose logs -f
Production Ready - Core features implemented with enterprise standards:
- β Complete microservices architecture
- β Frontend with modern React patterns
- β Event-driven communication
- β Comprehensive testing coverage
- β Production deployment configuration
- π Advanced analytics dashboard
- π Mobile application
- π Third-party integrations (Stripe, AWS)
Full-Stack Developer Portfolio
Demonstrating expertise in:
- Microservices Architecture - Event-driven design
- Modern React Development - Hooks, Context, Performance
- Enterprise Patterns - SOLID, DDD, CQRS
- DevOps & Infrastructure - Docker, Kafka, Monitoring
- Database Design - PostgreSQL, MongoDB, Redis
Enterprise-grade invoice management system showcasing production-ready microservices architecture, advanced React patterns, and scalable design principles.