๐๏ธ Patrones Arquitectรณnicos para Proyectos de Software¶
Esta guรญa presenta diferentes patrones arquitectรณnicos que puedes aplicar en tus proyectos, con ejemplos especรญficos para cada tecnologรญa y sus mejores prรกcticas.
๐ รndice de Patrones¶
- MVC (Model-View-Controller)
- Arquitectura Hexagonal (Ports & Adapters)
- Clean Architecture
- Arquitectura por Capas (Layered)
- CQRS (Command Query Responsibility Segregation)
- Event-Driven Architecture
- Microservicios
MVC (Model-View-Controller)¶
๐ Descripciรณn¶
Patrรณn que separa la aplicaciรณn en tres componentes principales: Modelo (datos), Vista (interfaz) y Controlador (lรณgica de control).
๐ฏ Cuรกndo Usar¶
- Aplicaciones web tradicionales
- APIs REST simples
- Proyectos con equipos pequeรฑos
- Aplicaciones CRUD bรกsicas
๐ Estructura por Tecnologรญa¶
.NET Web API (MVC)¶
MyApp.WebApi/
โโโ Controllers/
โ โโโ BaseController.cs
โ โโโ UsersController.cs
โ โโโ ProductsController.cs
โโโ Models/
โ โโโ User.cs
โ โโโ Product.cs
โ โโโ ViewModels/
โ โ โโโ UserViewModel.cs
โ โ โโโ ProductViewModel.cs
โ โโโ DTOs/
โ โโโ CreateUserDto.cs
โ โโโ UpdateUserDto.cs
โโโ Views/ (si usas Razor)
โ โโโ Users/
โ โโโ Products/
โโโ Services/
โ โโโ IUserService.cs
โ โโโ UserService.cs
โโโ Data/
โ โโโ ApplicationDbContext.cs
โ โโโ Repositories/
โ โโโ IUserRepository.cs
โ โโโ UserRepository.cs
โโโ Program.cs
Java Spring Boot (MVC)¶
src/main/java/com/myapp/
โโโ controller/
โ โโโ BaseController.java
โ โโโ UserController.java
โ โโโ ProductController.java
โโโ model/
โ โโโ entity/
โ โ โโโ User.java
โ โ โโโ Product.java
โ โโโ dto/
โ โโโ UserDto.java
โ โโโ ProductDto.java
โโโ service/
โ โโโ UserService.java
โ โโโ ProductService.java
โโโ repository/
โ โโโ UserRepository.java
โ โโโ ProductRepository.java
โโโ config/
โ โโโ DatabaseConfig.java
โโโ MyAppApplication.java
Node.js/Express (MVC)¶
src/
โโโ controllers/
โ โโโ baseController.js
โ โโโ userController.js
โ โโโ productController.js
โโโ models/
โ โโโ User.js
โ โโโ Product.js
โ โโโ index.js
โโโ views/ (si usas templates)
โ โโโ users/
โ โโโ products/
โโโ services/
โ โโโ userService.js
โ โโโ productService.js
โโโ routes/
โ โโโ userRoutes.js
โ โโโ productRoutes.js
โโโ middleware/
โ โโโ authMiddleware.js
โโโ app.js
โ Mejores Prรกcticas MVC¶
Controladores¶
// .NET - Controlador delgado
[ApiController]
[Route("api/[controller]")]
public class UsersController : BaseController
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _userService.GetUserByIdAsync(id);
return user != null ? Ok(user) : NotFound();
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser(CreateUserDto dto)
{
var user = await _userService.CreateUserAsync(dto);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
}
Servicios¶
// .NET - Lรณgica de negocio en servicios
public class UserService : IUserService
{
private readonly IUserRepository _repository;
private readonly IMapper _mapper;
public UserService(IUserRepository repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
public async Task<UserDto> CreateUserAsync(CreateUserDto dto)
{
// Validaciones de negocio
if (await _repository.ExistsByEmailAsync(dto.Email))
throw new BusinessException("Email already exists");
var user = _mapper.Map<User>(dto);
user.CreatedAt = DateTime.UtcNow;
await _repository.AddAsync(user);
return _mapper.Map<UserDto>(user);
}
}
Arquitectura Hexagonal (Ports & Adapters)¶
๐ Descripciรณn¶
Aรญsla la lรณgica de negocio del mundo exterior mediante puertos (interfaces) y adaptadores (implementaciones).
๐ฏ Cuรกndo Usar¶
- Aplicaciones complejas con mรบltiples integraciones
- Sistemas que requieren alta testabilidad
- Proyectos con requisitos de escalabilidad
- Cuando necesitas independencia de frameworks
๐ Estructura por Tecnologรญa¶
.NET (Arquitectura Hexagonal)¶
MyApp/
โโโ MyApp.Domain/ # Nรบcleo de la aplicaciรณn
โ โโโ Entities/
โ โ โโโ User.cs
โ โ โโโ Product.cs
โ โโโ ValueObjects/
โ โ โโโ Email.cs
โ โ โโโ Money.cs
โ โโโ Ports/ # Interfaces (Puertos)
โ โ โโโ IUserRepository.cs
โ โ โโโ IEmailService.cs
โ โ โโโ IPaymentGateway.cs
โ โโโ Services/ # Servicios de dominio
โ โโโ UserDomainService.cs
โ โโโ OrderDomainService.cs
โโโ MyApp.Application/ # Casos de uso
โ โโโ UseCases/
โ โ โโโ CreateUser/
โ โ โ โโโ CreateUserCommand.cs
โ โ โ โโโ CreateUserHandler.cs
โ โ โ โโโ CreateUserValidator.cs
โ โ โโโ GetUser/
โ โ โโโ GetUserQuery.cs
โ โ โโโ GetUserHandler.cs
โ โโโ DTOs/
โ โโโ UserDto.cs
โ โโโ ProductDto.cs
โโโ MyApp.Infrastructure/ # Adaptadores
โ โโโ Persistence/
โ โ โโโ ApplicationDbContext.cs
โ โ โโโ Repositories/
โ โ โ โโโ UserRepository.cs # Implementa IUserRepository
โ โ โ โโโ ProductRepository.cs
โ โ โโโ Configurations/
โ โ โโโ UserConfiguration.cs
โ โโโ ExternalServices/
โ โ โโโ EmailService.cs # Implementa IEmailService
โ โ โโโ PaymentGateway.cs # Implementa IPaymentGateway
โ โโโ Messaging/
โ โโโ EventBus.cs
โโโ MyApp.WebApi/ # Adaptador de entrada
โโโ Controllers/
โ โโโ UsersController.cs
โ โโโ ProductsController.cs
โโโ Middleware/
โ โโโ ExceptionMiddleware.cs
โโโ Program.cs
Java (Arquitectura Hexagonal)¶
src/main/java/com/myapp/
โโโ domain/ # Nรบcleo
โ โโโ model/
โ โ โโโ User.java
โ โ โโโ Product.java
โ โโโ port/
โ โ โโโ in/ # Puertos de entrada
โ โ โ โโโ CreateUserUseCase.java
โ โ โ โโโ GetUserUseCase.java
โ โ โโโ out/ # Puertos de salida
โ โ โโโ UserRepository.java
โ โ โโโ EmailService.java
โ โโโ service/
โ โโโ UserService.java # Implementa casos de uso
โ โโโ ProductService.java
โโโ infrastructure/ # Adaptadores
โ โโโ persistence/
โ โ โโโ UserJpaRepository.java # Implementa UserRepository
โ โ โโโ UserEntity.java
โ โโโ messaging/
โ โ โโโ EmailServiceImpl.java # Implementa EmailService
โ โโโ configuration/
โ โโโ BeanConfiguration.java
โโโ application/ # Adaptador de entrada
โโโ controller/
โ โโโ UserController.java
โ โโโ ProductController.java
โโโ MyAppApplication.java
โ Mejores Prรกcticas Hexagonal¶
Definiciรณn de Puertos¶
// Puerto de salida (Domain)
public interface IUserRepository
{
Task<User> GetByIdAsync(UserId id);
Task<User> GetByEmailAsync(Email email);
Task SaveAsync(User user);
Task<bool> ExistsByEmailAsync(Email email);
}
// Puerto de entrada (Application)
public interface ICreateUserUseCase
{
Task<UserDto> ExecuteAsync(CreateUserCommand command);
}
Implementaciรณn de Casos de Uso¶
// Application Layer
public class CreateUserHandler : ICreateUserUseCase
{
private readonly IUserRepository _userRepository;
private readonly IEmailService _emailService;
private readonly IDomainEventDispatcher _eventDispatcher;
public CreateUserHandler(
IUserRepository userRepository,
IEmailService emailService,
IDomainEventDispatcher eventDispatcher)
{
_userRepository = userRepository;
_emailService = emailService;
_eventDispatcher = eventDispatcher;
}
public async Task<UserDto> ExecuteAsync(CreateUserCommand command)
{
// Validar reglas de negocio
var email = new Email(command.Email);
if (await _userRepository.ExistsByEmailAsync(email))
throw new DomainException("Email already exists");
// Crear entidad de dominio
var user = User.Create(
command.FirstName,
command.LastName,
email);
// Persistir
await _userRepository.SaveAsync(user);
// Enviar eventos de dominio
await _eventDispatcher.DispatchAsync(user.DomainEvents);
// Enviar email de bienvenida
await _emailService.SendWelcomeEmailAsync(user.Email);
return UserDto.FromDomain(user);
}
}
Clean Architecture¶
๐ Descripciรณn¶
Arquitectura en capas concรฉntricas donde las dependencias apuntan hacia adentro, manteniendo el nรบcleo independiente de frameworks y tecnologรญas externas.
๐ฏ Cuรกndo Usar¶
- Aplicaciones empresariales complejas
- Sistemas con ciclo de vida largo
- Proyectos que requieren alta mantenibilidad
- Aplicaciones con mรบltiples interfaces (web, mobile, desktop)
๐ Estructura por Tecnologรญa¶
.NET (Clean Architecture)¶
MyApp/
โโโ MyApp.Domain/ # Capa mรกs interna
โ โโโ Entities/
โ โ โโโ BaseEntity.cs
โ โ โโโ User.cs
โ โ โโโ Product.cs
โ โโโ ValueObjects/
โ โ โโโ Email.cs
โ โ โโโ Money.cs
โ โ โโโ Address.cs
โ โโโ Enums/
โ โ โโโ UserStatus.cs
โ โ โโโ OrderStatus.cs
โ โโโ Events/
โ โ โโโ UserCreatedEvent.cs
โ โ โโโ OrderPlacedEvent.cs
โ โโโ Exceptions/
โ โ โโโ DomainException.cs
โ โ โโโ BusinessRuleException.cs
โ โโโ Interfaces/
โ โโโ IDomainEventDispatcher.cs
โโโ MyApp.Application/ # Casos de uso
โ โโโ Common/
โ โ โโโ Interfaces/
โ โ โ โโโ IApplicationDbContext.cs
โ โ โ โโโ IEmailService.cs
โ โ โ โโโ ICurrentUserService.cs
โ โ โโโ Models/
โ โ โ โโโ Result.cs
โ โ โ โโโ PaginatedList.cs
โ โ โโโ Behaviors/
โ โ โโโ ValidationBehavior.cs
โ โ โโโ LoggingBehavior.cs
โ โโโ Users/
โ โ โโโ Commands/
โ โ โ โโโ CreateUser/
โ โ โ โ โโโ CreateUserCommand.cs
โ โ โ โ โโโ CreateUserCommandHandler.cs
โ โ โ โ โโโ CreateUserCommandValidator.cs
โ โ โ โโโ UpdateUser/
โ โ โ โโโ UpdateUserCommand.cs
โ โ โ โโโ UpdateUserCommandHandler.cs
โ โ โโโ Queries/
โ โ โ โโโ GetUser/
โ โ โ โ โโโ GetUserQuery.cs
โ โ โ โ โโโ GetUserQueryHandler.cs
โ โ โ โโโ GetUsers/
โ โ โ โโโ GetUsersQuery.cs
โ โ โ โโโ GetUsersQueryHandler.cs
โ โ โโโ DTOs/
โ โ โโโ UserDto.cs
โ โ โโโ UserListDto.cs
โ โโโ Products/
โ โโโ Commands/
โ โโโ Queries/
โ โโโ DTOs/
โโโ MyApp.Infrastructure/ # Implementaciones externas
โ โโโ Persistence/
โ โ โโโ ApplicationDbContext.cs
โ โ โโโ Repositories/
โ โ โ โโโ UserRepository.cs
โ โ โ โโโ ProductRepository.cs
โ โ โโโ Configurations/
โ โ โ โโโ UserConfiguration.cs
โ โ โ โโโ ProductConfiguration.cs
โ โ โโโ Migrations/
โ โโโ Services/
โ โ โโโ EmailService.cs
โ โ โโโ CurrentUserService.cs
โ โ โโโ DateTimeService.cs
โ โโโ Identity/
โ โ โโโ IdentityService.cs
โ โ โโโ ApplicationUser.cs
โ โโโ ExternalServices/
โ โโโ PaymentGateway.cs
โ โโโ NotificationService.cs
โโโ MyApp.WebApi/ # Capa de presentaciรณn
โโโ Controllers/
โ โโโ BaseController.cs
โ โโโ UsersController.cs
โ โโโ ProductsController.cs
โโโ Filters/
โ โโโ ApiExceptionFilterAttribute.cs
โ โโโ ValidationFilterAttribute.cs
โโโ Services/
โ โโโ CurrentUserService.cs
โโโ Extensions/
โ โโโ ServiceCollectionExtensions.cs
โ โโโ ApplicationBuilderExtensions.cs
โโโ Program.cs
Python (Clean Architecture)¶
myapp/
โโโ domain/ # Capa de dominio
โ โโโ entities/
โ โ โโโ __init__.py
โ โ โโโ base_entity.py
โ โ โโโ user.py
โ โ โโโ product.py
โ โโโ value_objects/
โ โ โโโ __init__.py
โ โ โโโ email.py
โ โ โโโ money.py
โ โโโ events/
โ โ โโโ __init__.py
โ โ โโโ user_created.py
โ โ โโโ order_placed.py
โ โโโ exceptions/
โ โโโ __init__.py
โ โโโ domain_exception.py
โ โโโ business_rule_exception.py
โโโ application/ # Casos de uso
โ โโโ common/
โ โ โโโ __init__.py
โ โ โโโ interfaces/
โ โ โ โโโ __init__.py
โ โ โ โโโ repository.py
โ โ โ โโโ email_service.py
โ โ โ โโโ unit_of_work.py
โ โ โโโ models/
โ โ โโโ __init__.py
โ โ โโโ result.py
โ โโโ users/
โ โ โโโ __init__.py
โ โ โโโ commands/
โ โ โ โโโ __init__.py
โ โ โ โโโ create_user.py
โ โ โ โโโ update_user.py
โ โ โโโ queries/
โ โ โ โโโ __init__.py
โ โ โ โโโ get_user.py
โ โ โ โโโ get_users.py
โ โ โโโ dtos/
โ โ โโโ __init__.py
โ โ โโโ user_dto.py
โ โโโ products/
โ โโโ commands/
โ โโโ queries/
โ โโโ dtos/
โโโ infrastructure/ # Implementaciones
โ โโโ __init__.py
โ โโโ persistence/
โ โ โโโ __init__.py
โ โ โโโ database.py
โ โ โโโ repositories/
โ โ โ โโโ __init__.py
โ โ โ โโโ user_repository.py
โ โ โ โโโ product_repository.py
โ โ โโโ models/
โ โ โโโ __init__.py
โ โ โโโ user_model.py
โ โ โโโ product_model.py
โ โโโ services/
โ โ โโโ __init__.py
โ โ โโโ email_service.py
โ โ โโโ notification_service.py
โ โโโ external/
โ โโโ __init__.py
โ โโโ payment_gateway.py
โโโ presentation/ # Capa de presentaciรณn
โโโ __init__.py
โโโ api/
โ โโโ __init__.py
โ โโโ dependencies.py
โ โโโ routers/
โ โ โโโ __init__.py
โ โ โโโ users.py
โ โ โโโ products.py
โ โโโ middleware/
โ โโโ __init__.py
โ โโโ exception_handler.py
โโโ main.py
โ Mejores Prรกcticas Clean Architecture¶
Entidades de Dominio¶
// Domain Layer - Entidad rica en comportamiento
public class User : BaseEntity
{
private readonly List<DomainEvent> _domainEvents = new();
public string FirstName { get; private set; }
public string LastName { get; private set; }
public Email Email { get; private set; }
public UserStatus Status { get; private set; }
private User() { } // Para EF Core
private User(string firstName, string lastName, Email email)
{
FirstName = firstName;
LastName = lastName;
Email = email;
Status = UserStatus.Active;
CreatedAt = DateTime.UtcNow;
AddDomainEvent(new UserCreatedEvent(this));
}
public static User Create(string firstName, string lastName, string email)
{
var emailVO = new Email(email);
return new User(firstName, lastName, emailVO);
}
public void UpdateEmail(string newEmail)
{
var emailVO = new Email(newEmail);
if (Email.Equals(emailVO))
return;
Email = emailVO;
AddDomainEvent(new UserEmailChangedEvent(this, emailVO));
}
public void Deactivate()
{
if (Status == UserStatus.Inactive)
throw new DomainException("User is already inactive");
Status = UserStatus.Inactive;
AddDomainEvent(new UserDeactivatedEvent(this));
}
private void AddDomainEvent(DomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public IReadOnlyCollection<DomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void ClearDomainEvents()
{
_domainEvents.Clear();
}
}
Casos de Uso con CQRS¶
// Application Layer - Command Handler
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Result<UserDto>>
{
private readonly IApplicationDbContext _context;
private readonly IEmailService _emailService;
private readonly ILogger<CreateUserCommandHandler> _logger;
public CreateUserCommandHandler(
IApplicationDbContext context,
IEmailService emailService,
ILogger<CreateUserCommandHandler> logger)
{
_context = context;
_emailService = emailService;
_logger = logger;
}
public async Task<Result<UserDto>> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
try
{
// Verificar si el email ya existe
var existingUser = await _context.Users
.FirstOrDefaultAsync(u => u.Email.Value == request.Email, cancellationToken);
if (existingUser != null)
return Result<UserDto>.Failure("Email already exists");
// Crear nueva entidad
var user = User.Create(request.FirstName, request.LastName, request.Email);
// Persistir
_context.Users.Add(user);
await _context.SaveChangesAsync(cancellationToken);
// Enviar email de bienvenida
await _emailService.SendWelcomeEmailAsync(user.Email.Value, user.FirstName);
_logger.LogInformation("User created successfully with ID: {UserId}", user.Id);
return Result<UserDto>.Success(UserDto.FromDomain(user));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating user with email: {Email}", request.Email);
return Result<UserDto>.Failure("An error occurred while creating the user");
}
}
}
Arquitectura por Capas (Layered)¶
๐ Descripciรณn¶
Organiza el cรณdigo en capas horizontales donde cada capa solo puede comunicarse con la capa inmediatamente inferior.
๐ฏ Cuรกndo Usar¶
- Aplicaciones empresariales tradicionales
- Sistemas con separaciรณn clara de responsabilidades
- Proyectos con equipos especializados por capa
- Aplicaciones con requisitos de auditorรญa
๐ Estructura por Tecnologรญa¶
Java Spring Boot (Capas)¶
src/main/java/com/myapp/
โโโ presentation/ # Capa de Presentaciรณn
โ โโโ controller/
โ โ โโโ UserController.java
โ โ โโโ ProductController.java
โ โโโ dto/
โ โ โโโ request/
โ โ โ โโโ CreateUserRequest.java
โ โ โ โโโ UpdateUserRequest.java
โ โ โโโ response/
โ โ โโโ UserResponse.java
โ โ โโโ ProductResponse.java
โ โโโ mapper/
โ โโโ UserMapper.java
โ โโโ ProductMapper.java
โโโ business/ # Capa de Negocio
โ โโโ service/
โ โ โโโ UserService.java
โ โ โโโ ProductService.java
โ โ โโโ OrderService.java
โ โโโ validator/
โ โ โโโ UserValidator.java
โ โ โโโ ProductValidator.java
โ โโโ exception/
โ โโโ BusinessException.java
โ โโโ ValidationException.java
โโโ persistence/ # Capa de Persistencia
โ โโโ repository/
โ โ โโโ UserRepository.java
โ โ โโโ ProductRepository.java
โ โโโ entity/
โ โ โโโ UserEntity.java
โ โ โโโ ProductEntity.java
โ โโโ config/
โ โโโ DatabaseConfig.java
โโโ infrastructure/ # Capa de Infraestructura
โโโ security/
โ โโโ SecurityConfig.java
โ โโโ JwtTokenProvider.java
โโโ messaging/
โ โโโ EmailService.java
โ โโโ NotificationService.java
โโโ external/
โโโ PaymentGateway.java
โโโ ExternalApiClient.java
Node.js (Capas)¶
src/
โโโ presentation/ # Capa de Presentaciรณn
โ โโโ controllers/
โ โ โโโ userController.js
โ โ โโโ productController.js
โ โโโ middleware/
โ โ โโโ authMiddleware.js
โ โ โโโ validationMiddleware.js
โ โ โโโ errorMiddleware.js
โ โโโ routes/
โ โ โโโ userRoutes.js
โ โ โโโ productRoutes.js
โ โโโ validators/
โ โโโ userValidator.js
โ โโโ productValidator.js
โโโ business/ # Capa de Negocio
โ โโโ services/
โ โ โโโ userService.js
โ โ โโโ productService.js
โ โ โโโ orderService.js
โ โโโ models/
โ โ โโโ User.js
โ โ โโโ Product.js
โ โ โโโ Order.js
โ โโโ exceptions/
โ โโโ BusinessError.js
โ โโโ ValidationError.js
โโโ persistence/ # Capa de Persistencia
โ โโโ repositories/
โ โ โโโ userRepository.js
โ โ โโโ productRepository.js
โ โ โโโ baseRepository.js
โ โโโ entities/
โ โ โโโ userEntity.js
โ โ โโโ productEntity.js
โ โโโ database/
โ โโโ connection.js
โ โโโ migrations/
โโโ infrastructure/ # Capa de Infraestructura
โโโ security/
โ โโโ jwtService.js
โ โโโ encryptionService.js
โโโ messaging/
โ โโโ emailService.js
โ โโโ smsService.js
โโโ external/
โโโ paymentGateway.js
โโโ externalApiClient.js
โ Mejores Prรกcticas por Capas¶
Capa de Presentaciรณn¶
// Node.js - Controlador delgado
class UserController {
constructor(userService) {
this.userService = userService;
}
async createUser(req, res, next) {
try {
// Solo manejo de HTTP, delegaciรณn a capa de negocio
const userData = req.body;
const user = await this.userService.createUser(userData);
res.status(201).json({
success: true,
data: user,
message: 'User created successfully'
});
} catch (error) {
next(error); // Delegar manejo de errores al middleware
}
}
async getUsers(req, res, next) {
try {
const { page = 1, limit = 10, search } = req.query;
const users = await this.userService.getUsers({ page, limit, search });
res.json({
success: true,
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: users.total
}
});
} catch (error) {
next(error);
}
}
}
Capa de Negocio¶
// Node.js - Servicio con lรณgica de negocio
class UserService {
constructor(userRepository, emailService, validationService) {
this.userRepository = userRepository;
this.emailService = emailService;
this.validationService = validationService;
}
async createUser(userData) {
// Validaciones de negocio
await this.validationService.validateUserData(userData);
// Verificar reglas de negocio
const existingUser = await this.userRepository.findByEmail(userData.email);
if (existingUser) {
throw new BusinessError('Email already exists', 'DUPLICATE_EMAIL');
}
// Crear usuario
const user = new User({
...userData,
createdAt: new Date(),
status: 'ACTIVE'
});
// Persistir
const savedUser = await this.userRepository.save(user);
// Procesos adicionales
await this.emailService.sendWelcomeEmail(savedUser.email, savedUser.name);
return savedUser.toDTO();
}
async getUsers({ page, limit, search }) {
// Lรณgica de bรบsqueda y paginaciรณn
const filters = {};
if (search) {
filters.name = { $regex: search, $options: 'i' };
}
const users = await this.userRepository.findPaginated(filters, page, limit);
return {
users: users.data.map(user => user.toDTO()),
total: users.total,
page,
limit
};
}
}
Capa de Persistencia¶
// Node.js - Repositorio
class UserRepository extends BaseRepository {
constructor(database) {
super(database, 'users');
}
async findByEmail(email) {
return await this.collection.findOne({ email });
}
async findPaginated(filters, page, limit) {
const skip = (page - 1) * limit;
const [data, total] = await Promise.all([
this.collection.find(filters).skip(skip).limit(limit).toArray(),
this.collection.countDocuments(filters)
]);
return { data, total };
}
async save(user) {
if (user.id) {
await this.collection.updateOne(
{ _id: user.id },
{ $set: user.toDocument() }
);
} else {
const result = await this.collection.insertOne(user.toDocument());
user.id = result.insertedId;
}
return user;
}
}
CQRS (Command Query Responsibility Segregation)¶
๐ Descripciรณn¶
Separa las operaciones de lectura (queries) de las operaciones de escritura (commands), permitiendo optimizar cada una independientemente.
๐ฏ Cuรกndo Usar¶
- Sistemas con alta carga de lectura vs escritura
- Aplicaciones que requieren diferentes modelos de datos para lectura y escritura
- Sistemas con requisitos de escalabilidad complejos
- Aplicaciones con Event Sourcing
๐ Estructura por Tecnologรญa¶
.NET (CQRS con MediatR)¶
MyApp/
โโโ MyApp.Application/
โ โโโ Commands/ # Operaciones de escritura
โ โ โโโ Users/
โ โ โ โโโ CreateUser/
โ โ โ โ โโโ CreateUserCommand.cs
โ โ โ โ โโโ CreateUserCommandHandler.cs
โ โ โ โ โโโ CreateUserCommandValidator.cs
โ โ โ โโโ UpdateUser/
โ โ โ โ โโโ UpdateUserCommand.cs
โ โ โ โ โโโ UpdateUserCommandHandler.cs
โ โ โ โโโ DeleteUser/
โ โ โ โโโ DeleteUserCommand.cs
โ โ โ โโโ DeleteUserCommandHandler.cs
โ โ โโโ Products/
โ โ โโโ CreateProduct/
โ โ โโโ UpdateProduct/
โ โ โโโ DeleteProduct/
โ โโโ Queries/ # Operaciones de lectura
โ โ โโโ Users/
โ โ โ โโโ GetUser/
โ โ โ โ โโโ GetUserQuery.cs
โ โ โ โ โโโ GetUserQueryHandler.cs
โ โ โ โ โโโ UserDto.cs
โ โ โ โโโ GetUsers/
โ โ โ โ โโโ GetUsersQuery.cs
โ โ โ โ โโโ GetUsersQueryHandler.cs
โ โ โ โ โโโ UserListDto.cs
โ โ โ โโโ SearchUsers/
โ โ โ โโโ SearchUsersQuery.cs
โ โ โ โโโ SearchUsersQueryHandler.cs
โ โ โโโ Products/
โ โ โโโ GetProduct/
โ โ โโโ GetProducts/
โ โ โโโ SearchProducts/
โ โโโ Common/
โ โ โโโ Interfaces/
โ โ โ โโโ ICommand.cs
โ โ โ โโโ IQuery.cs
โ โ โ โโโ ICommandHandler.cs
โ โ โ โโโ IQueryHandler.cs
โ โ โโโ Behaviors/
โ โ โโโ ValidationBehavior.cs
โ โ โโโ LoggingBehavior.cs
โ โ โโโ PerformanceBehavior.cs
โ โโโ Events/
โ โโโ UserCreatedEvent.cs
โ โโโ UserUpdatedEvent.cs
โ โโโ EventHandlers/
โ โโโ UserCreatedEventHandler.cs
โ โโโ UserUpdatedEventHandler.cs
โโโ MyApp.Infrastructure/
โ โโโ Persistence/
โ โ โโโ Write/ # Base de datos de escritura
โ โ โ โโโ WriteDbContext.cs
โ โ โ โโโ Repositories/
โ โ โ โโโ UserWriteRepository.cs
โ โ โ โโโ ProductWriteRepository.cs
โ โ โโโ Read/ # Base de datos de lectura
โ โ โโโ ReadDbContext.cs
โ โ โโโ Repositories/
โ โ โโโ UserReadRepository.cs
โ โ โโโ ProductReadRepository.cs
โ โโโ EventStore/
โ โโโ EventStoreRepository.cs
โ โโโ EventProjections/
โ โโโ UserProjection.cs
โ โโโ ProductProjection.cs
โโโ MyApp.WebApi/
โโโ Controllers/
โ โโโ UsersController.cs # Maneja Commands y Queries
โ โโโ ProductsController.cs
โโโ Program.cs
Python (CQRS)¶
myapp/
โโโ application/
โ โโโ commands/ # Operaciones de escritura
โ โ โโโ __init__.py
โ โ โโโ base.py
โ โ โโโ users/
โ โ โ โโโ __init__.py
โ โ โ โโโ create_user.py
โ โ โ โโโ update_user.py
โ โ โ โโโ delete_user.py
โ โ โโโ products/
โ โ โโโ create_product.py
โ โ โโโ update_product.py
โ โ โโโ delete_product.py
โ โโโ queries/ # Operaciones de lectura
โ โ โโโ __init__.py
โ โ โโโ base.py
โ โ โโโ users/
โ โ โ โโโ __init__.py
โ โ โ โโโ get_user.py
โ โ โ โโโ get_users.py
โ โ โ โโโ search_users.py
โ โ โโโ products/
โ โ โโโ get_product.py
โ โ โโโ get_products.py
โ โ โโโ search_products.py
โ โโโ handlers/
โ โ โโโ __init__.py
โ โ โโโ command_handlers.py
โ โ โโโ query_handlers.py
โ โ โโโ event_handlers.py
โ โโโ events/
โ โโโ __init__.py
โ โโโ user_events.py
โ โโโ product_events.py
โโโ infrastructure/
โ โโโ persistence/
โ โ โโโ write/ # Base de datos de escritura
โ โ โ โโโ __init__.py
โ โ โ โโโ write_db.py
โ โ โ โโโ repositories/
โ โ โ โโโ user_write_repo.py
โ โ โ โโโ product_write_repo.py
โ โ โโโ read/ # Base de datos de lectura
โ โ โโโ __init__.py
โ โ โโโ read_db.py
โ โ โโโ repositories/
โ โ โโโ user_read_repo.py
โ โ โโโ product_read_repo.py
โ โโโ event_store/
โ โโโ __init__.py
โ โโโ event_store.py
โ โโโ projections/
โ โโโ user_projection.py
โ โโโ product_projection.py
โโโ presentation/
โโโ api/
โ โโโ routers/
โ โ โโโ users.py # Maneja Commands y Queries
โ โ โโโ products.py
โ โโโ dependencies.py
โโโ main.py
โ Mejores Prรกcticas CQRS¶
Commands (Escritura)¶
// Command
public record CreateUserCommand(
string FirstName,
string LastName,
string Email) : IRequest<Result<int>>;
// Command Handler
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Result<int>>
{
private readonly IWriteDbContext _writeContext;
private readonly IEventBus _eventBus;
private readonly ILogger<CreateUserCommandHandler> _logger;
public CreateUserCommandHandler(
IWriteDbContext writeContext,
IEventBus eventBus,
ILogger<CreateUserCommandHandler> logger)
{
_writeContext = writeContext;
_eventBus = eventBus;
_logger = logger;
}
public async Task<Result<int>> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
try
{
// Validar reglas de negocio
var existingUser = await _writeContext.Users
.FirstOrDefaultAsync(u => u.Email == request.Email, cancellationToken);
if (existingUser != null)
return Result<int>.Failure("Email already exists");
// Crear entidad
var user = new User
{
FirstName = request.FirstName,
LastName = request.LastName,
Email = request.Email,
CreatedAt = DateTime.UtcNow,
Status = UserStatus.Active
};
// Persistir en base de escritura
_writeContext.Users.Add(user);
await _writeContext.SaveChangesAsync(cancellationToken);
// Publicar evento para actualizar base de lectura
await _eventBus.PublishAsync(new UserCreatedEvent
{
UserId = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
CreatedAt = user.CreatedAt
}, cancellationToken);
_logger.LogInformation("User created with ID: {UserId}", user.Id);
return Result<int>.Success(user.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating user");
return Result<int>.Failure("An error occurred while creating the user");
}
}
}
Queries (Lectura)¶
// Query
public record GetUsersQuery(
int Page = 1,
int PageSize = 10,
string? Search = null) : IRequest<Result<PagedList<UserListDto>>>;
// Query Handler
public class GetUsersQueryHandler : IRequestHandler<GetUsersQuery, Result<PagedList<UserListDto>>>
{
private readonly IReadDbContext _readContext;
private readonly IMemoryCache _cache;
private readonly ILogger<GetUsersQueryHandler> _logger;
public GetUsersQueryHandler(
IReadDbContext readContext,
IMemoryCache cache,
ILogger<GetUsersQueryHandler> logger)
{
_readContext = readContext;
_cache = cache;
_logger = logger;
}
public async Task<Result<PagedList<UserListDto>>> Handle(GetUsersQuery request, CancellationToken cancellationToken)
{
try
{
var cacheKey = $"users_page_{request.Page}_size_{request.PageSize}_search_{request.Search}";
if (_cache.TryGetValue(cacheKey, out PagedList<UserListDto>? cachedResult))
{
_logger.LogDebug("Returning cached users for page {Page}", request.Page);
return Result<PagedList<UserListDto>>.Success(cachedResult!);
}
// Query optimizada para lectura
var query = _readContext.UserReadModels.AsQueryable();
if (!string.IsNullOrEmpty(request.Search))
{
query = query.Where(u =>
u.FullName.Contains(request.Search) ||
u.Email.Contains(request.Search));
}
var totalCount = await query.CountAsync(cancellationToken);
var users = await query
.OrderBy(u => u.FullName)
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.Select(u => new UserListDto
{
Id = u.Id,
FullName = u.FullName,
Email = u.Email,
Status = u.Status,
CreatedAt = u.CreatedAt
})
.ToListAsync(cancellationToken);
var result = new PagedList<UserListDto>(users, totalCount, request.Page, request.PageSize);
// Cache por 5 minutos
_cache.Set(cacheKey, result, TimeSpan.FromMinutes(5));
return Result<PagedList<UserListDto>>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving users");
return Result<PagedList<UserListDto>>.Failure("An error occurred while retrieving users");
}
}
}
Event Handlers para Sincronizaciรณn¶
// Event Handler para mantener sincronizada la base de lectura
public class UserCreatedEventHandler : INotificationHandler<UserCreatedEvent>
{
private readonly IReadDbContext _readContext;
private readonly ILogger<UserCreatedEventHandler> _logger;
public UserCreatedEventHandler(
IReadDbContext readContext,
ILogger<UserCreatedEventHandler> logger)
{
_readContext = readContext;
_logger = logger;
}
public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
{
try
{
// Crear modelo optimizado para lectura
var userReadModel = new UserReadModel
{
Id = notification.UserId,
FullName = $"{notification.FirstName} {notification.LastName}",
Email = notification.Email,
Status = "Active",
CreatedAt = notification.CreatedAt,
// Campos adicionales optimizados para queries
SearchText = $"{notification.FirstName} {notification.LastName} {notification.Email}".ToLower()
};
_readContext.UserReadModels.Add(userReadModel);
await _readContext.SaveChangesAsync(cancellationToken);
_logger.LogInformation("User read model created for user ID: {UserId}", notification.UserId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating user read model for user ID: {UserId}", notification.UserId);
throw; // Re-throw para que el event bus pueda manejar el retry
}
}
}
Event-Driven Architecture¶
๐ Descripciรณn¶
Arquitectura basada en la producciรณn, detecciรณn y reacciรณn a eventos, promoviendo el desacoplamiento entre componentes.
๐ฏ Cuรกndo Usar¶
- Sistemas distribuidos complejos
- Aplicaciones que requieren alta escalabilidad
- Microservicios que necesitan comunicaciรณn asรญncrona
- Sistemas con workflows complejos
๐ Estructura por Tecnologรญa¶
.NET (Event-Driven)¶
MyApp/
โโโ MyApp.Events/ # Definiciones de eventos
โ โโโ Common/
โ โ โโโ IEvent.cs
โ โ โโโ IDomainEvent.cs
โ โ โโโ IIntegrationEvent.cs
โ โโโ Domain/
โ โ โโโ UserEvents/
โ โ โ โโโ UserCreatedEvent.cs
โ โ โ โโโ UserUpdatedEvent.cs
โ โ โ โโโ UserDeletedEvent.cs
โ โ โโโ OrderEvents/
โ โ โโโ OrderPlacedEvent.cs
โ โ โโโ OrderConfirmedEvent.cs
โ โ โโโ OrderCancelledEvent.cs
โ โโโ Integration/
โ โโโ UserRegisteredIntegrationEvent.cs
โ โโโ OrderProcessedIntegrationEvent.cs
โ โโโ PaymentCompletedIntegrationEvent.cs
โโโ MyApp.EventHandlers/ # Manejadores de eventos
โ โโโ Domain/
โ โ โโโ UserEventHandlers/
โ โ โ โโโ UserCreatedEventHandler.cs
โ โ โ โโโ UserUpdatedEventHandler.cs
โ โ โ โโโ SendWelcomeEmailHandler.cs
โ โ โโโ OrderEventHandlers/
โ โ โโโ OrderPlacedEventHandler.cs
โ โ โโโ UpdateInventoryHandler.cs
โ โโโ Integration/
โ โโโ UserRegisteredHandler.cs
โ โโโ OrderProcessedHandler.cs
โ โโโ PaymentCompletedHandler.cs
โโโ MyApp.EventBus/ # Infraestructura de eventos
โ โโโ Abstractions/
โ โ โโโ IEventBus.cs
โ โ โโโ IEventHandler.cs
โ โ โโโ IEventStore.cs
โ โโโ InMemory/
โ โ โโโ InMemoryEventBus.cs
โ โ โโโ InMemoryEventStore.cs
โ โโโ RabbitMQ/
โ โ โโโ RabbitMQEventBus.cs
โ โ โโโ RabbitMQConnection.cs
โ โโโ Azure/
โ โโโ ServiceBusEventBus.cs
โ โโโ EventHubEventStore.cs
โโโ MyApp.Projections/ # Proyecciones de eventos
โ โโโ UserProjections/
โ โ โโโ UserListProjection.cs
โ โ โโโ UserStatisticsProjection.cs
โ โโโ OrderProjections/
โ โโโ OrderHistoryProjection.cs
โ โโโ SalesReportProjection.cs
โโโ MyApp.Sagas/ # Procesos de negocio largos
โโโ OrderProcessingSaga.cs
โโโ UserOnboardingSaga.cs
โโโ PaymentProcessingSaga.cs
Node.js (Event-Driven)¶
src/
โโโ events/ # Definiciones de eventos
โ โโโ base/
โ โ โโโ event.js
โ โ โโโ domainEvent.js
โ โ โโโ integrationEvent.js
โ โโโ domain/
โ โ โโโ userEvents.js
โ โ โโโ orderEvents.js
โ โ โโโ productEvents.js
โ โโโ integration/
โ โโโ userRegisteredEvent.js
โ โโโ orderProcessedEvent.js
โ โโโ paymentCompletedEvent.js
โโโ eventHandlers/ # Manejadores de eventos
โ โโโ domain/
โ โ โโโ userEventHandlers.js
โ โ โโโ orderEventHandlers.js
โ โ โโโ productEventHandlers.js
โ โโโ integration/
โ โโโ userRegisteredHandler.js
โ โโโ orderProcessedHandler.js
โ โโโ paymentCompletedHandler.js
โโโ eventBus/ # Infraestructura de eventos
โ โโโ abstractions/
โ โ โโโ eventBus.js
โ โ โโโ eventHandler.js
โ โ โโโ eventStore.js
โ โโโ implementations/
โ โ โโโ inMemoryEventBus.js
โ โ โโโ redisEventBus.js
โ โ โโโ rabbitMQEventBus.js
โ โโโ middleware/
โ โโโ eventLogger.js
โ โโโ eventValidator.js
โ โโโ retryHandler.js
โโโ projections/ # Proyecciones de eventos
โ โโโ userProjections.js
โ โโโ orderProjections.js
โ โโโ analyticsProjections.js
โโโ sagas/ # Procesos de negocio largos
โ โโโ orderProcessingSaga.js
โ โโโ userOnboardingSaga.js
โ โโโ paymentProcessingSaga.js
โโโ workflows/ # Orquestaciรณn de eventos
โโโ userRegistrationWorkflow.js
โโโ orderFulfillmentWorkflow.js
โโโ paymentProcessingWorkflow.js
โ Mejores Prรกcticas Event-Driven¶
Definiciรณn de Eventos¶
// Evento de dominio
public record UserCreatedEvent : IDomainEvent
{
public Guid UserId { get; init; }
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
public DateTime CreatedAt { get; init; }
public Guid EventId { get; init; } = Guid.NewGuid();
public DateTime OccurredAt { get; init; } = DateTime.UtcNow;
}
// Evento de integraciรณn
public record UserRegisteredIntegrationEvent : IIntegrationEvent
{
public Guid UserId { get; init; }
public string Email { get; init; } = string.Empty;
public string FullName { get; init; } = string.Empty;
public DateTime RegistrationDate { get; init; }
public Guid EventId { get; init; } = Guid.NewGuid();
public DateTime OccurredAt { get; init; } = DateTime.UtcNow;
public string EventType { get; init; } = nameof(UserRegisteredIntegrationEvent);
public int Version { get; init; } = 1;
}
Event Handlers¶
// Handler para eventos de dominio
public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
private readonly IEmailService _emailService;
private readonly ILogger<SendWelcomeEmailHandler> _logger;
public SendWelcomeEmailHandler(
IEmailService emailService,
ILogger<SendWelcomeEmailHandler> logger)
{
_emailService = emailService;
_logger = logger;
}
public async Task Handle(UserCreatedEvent @event, CancellationToken cancellationToken)
{
try
{
await _emailService.SendWelcomeEmailAsync(
@event.Email,
@event.FirstName,
cancellationToken);
_logger.LogInformation(
"Welcome email sent to user {UserId} at {Email}",
@event.UserId,
@event.Email);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Failed to send welcome email to user {UserId}",
@event.UserId);
throw; // Re-throw para retry automรกtico
}
}
}
// Handler para eventos de integraciรณn
public class UserRegisteredHandler : IEventHandler<UserRegisteredIntegrationEvent>
{
private readonly IAnalyticsService _analyticsService;
private readonly ICrmService _crmService;
private readonly ILogger<UserRegisteredHandler> _logger;
public UserRegisteredHandler(
IAnalyticsService analyticsService,
ICrmService crmService,
ILogger<UserRegisteredHandler> logger)
{
_analyticsService = analyticsService;
_crmService = crmService;
_logger = logger;
}
public async Task Handle(UserRegisteredIntegrationEvent @event, CancellationToken cancellationToken)
{
try
{
// Actualizar analytics
await _analyticsService.TrackUserRegistrationAsync(@event.UserId, @event.RegistrationDate);
// Sincronizar con CRM
await _crmService.CreateContactAsync(new CreateContactRequest
{
ExternalId = @event.UserId.ToString(),
Email = @event.Email,
FullName = @event.FullName,
Source = "Application"
});
_logger.LogInformation(
"User registration processed for user {UserId}",
@event.UserId);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Failed to process user registration for user {UserId}",
@event.UserId);
throw;
}
}
}
Event Bus Implementation¶
public class EventBus : IEventBus
{
private readonly IServiceProvider _serviceProvider;
private readonly IEventStore _eventStore;
private readonly ILogger<EventBus> _logger;
private readonly Dictionary<Type, List<Type>> _handlers = new();
public EventBus(
IServiceProvider serviceProvider,
IEventStore eventStore,
ILogger<EventBus> logger)
{
_serviceProvider = serviceProvider;
_eventStore = eventStore;
_logger = logger;
}
public async Task PublishAsync<T>(T @event, CancellationToken cancellationToken = default)
where T : IEvent
{
try
{
// Almacenar evento
await _eventStore.SaveEventAsync(@event, cancellationToken);
// Obtener handlers registrados
var eventType = typeof(T);
if (!_handlers.ContainsKey(eventType))
{
_logger.LogWarning("No handlers registered for event type {EventType}", eventType.Name);
return;
}
var handlerTasks = new List<Task>();
foreach (var handlerType in _handlers[eventType])
{
var handler = _serviceProvider.GetService(handlerType);
if (handler != null)
{
var handleMethod = handlerType.GetMethod("Handle");
if (handleMethod != null)
{
var task = (Task)handleMethod.Invoke(handler, new object[] { @event, cancellationToken })!;
handlerTasks.Add(task);
}
}
}
// Ejecutar todos los handlers en paralelo
await Task.WhenAll(handlerTasks);
_logger.LogInformation(
"Event {EventType} with ID {EventId} published successfully",
eventType.Name,
@event.EventId);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Failed to publish event {EventType} with ID {EventId}",
typeof(T).Name,
@event.EventId);
throw;
}
}
public void Subscribe<T, TH>()
where T : IEvent
where TH : IEventHandler<T>
{
var eventType = typeof(T);
var handlerType = typeof(TH);
if (!_handlers.ContainsKey(eventType))
{
_handlers[eventType] = new List<Type>();
}
if (!_handlers[eventType].Contains(handlerType))
{
_handlers[eventType].Add(handlerType);
_logger.LogInformation(
"Handler {HandlerType} subscribed to event {EventType}",
handlerType.Name,
eventType.Name);
}
}
}
Microservicios¶
๐ Descripciรณn¶
Arquitectura que estructura una aplicaciรณn como una colecciรณn de servicios pequeรฑos, autรณnomos y desplegables independientemente.
๐ฏ Cuรกndo Usar¶
- Aplicaciones grandes y complejas
- Equipos de desarrollo distribuidos
- Necesidad de escalabilidad independiente
- Diferentes tecnologรญas por servicio
- Ciclos de despliegue independientes
๐ Estructura por Tecnologรญa¶
.NET (Microservicios)¶
MyApp.Microservices/
โโโ src/
โ โโโ Services/
โ โ โโโ User/
โ โ โ โโโ MyApp.User.API/
โ โ โ โ โโโ Controllers/
โ โ โ โ โโโ Program.cs
โ โ โ โ โโโ Dockerfile
โ โ โ โโโ MyApp.User.Application/
โ โ โ โ โโโ Commands/
โ โ โ โ โโโ Queries/
โ โ โ โ โโโ Services/
โ โ โ โโโ MyApp.User.Domain/
โ โ โ โ โโโ Entities/
โ โ โ โ โโโ ValueObjects/
โ โ โ โ โโโ Events/
โ โ โ โโโ MyApp.User.Infrastructure/
โ โ โ โโโ Persistence/
โ โ โ โโโ ExternalServices/
โ โ โ โโโ Messaging/
โ โ โโโ Order/
โ โ โ โโโ MyApp.Order.API/
โ โ โ โโโ MyApp.Order.Application/
โ โ โ โโโ MyApp.Order.Domain/
โ โ โ โโโ MyApp.Order.Infrastructure/
โ โ โโโ Payment/
โ โ โ โโโ MyApp.Payment.API/
โ โ โ โโโ MyApp.Payment.Application/
โ โ โ โโโ MyApp.Payment.Domain/
โ โ โ โโโ MyApp.Payment.Infrastructure/
โ โ โโโ Notification/
โ โ โโโ MyApp.Notification.API/
โ โ โโโ MyApp.Notification.Application/
โ โ โโโ MyApp.Notification.Domain/
โ โ โโโ MyApp.Notification.Infrastructure/
โ โโโ ApiGateways/
โ โ โโโ MyApp.Gateway.Web/
โ โ โ โโโ Program.cs
โ โ โ โโโ ocelot.json
โ โ โ โโโ Dockerfile
โ โ โโโ MyApp.Gateway.Mobile/
โ โ โโโ Program.cs
โ โ โโโ ocelot.json
โ โ โโโ Dockerfile
โ โโโ BuildingBlocks/ # Componentes compartidos
โ โ โโโ MyApp.Common/
โ โ โ โโโ Events/
โ โ โ โโโ Exceptions/
โ โ โ โโโ Extensions/
โ โ โ โโโ Models/
โ โ โโโ MyApp.EventBus/
โ โ โ โโโ Abstractions/
โ โ โ โโโ RabbitMQ/
โ โ โ โโโ ServiceBus/
โ โ โโโ MyApp.Logging/
โ โ โโโ Serilog/
โ โ โโโ Extensions/
โ โโโ WebApps/ # Aplicaciones cliente
โ โโโ MyApp.Web/
โ โ โโโ Controllers/
โ โ โโโ Services/
โ โ โโโ Program.cs
โ โโโ MyApp.Admin/
โ โโโ Controllers/
โ โโโ Services/
โ โโโ Program.cs
โโโ tests/
โ โโโ Services/
โ โ โโโ User.UnitTests/
โ โ โโโ User.IntegrationTests/
โ โ โโโ Order.UnitTests/
โ โ โโโ Order.IntegrationTests/
โ โโโ ApiGateways/
โ โโโ Gateway.IntegrationTests/
โโโ docker-compose.yml
โโโ docker-compose.override.yml
โโโ k8s/ # Kubernetes manifests
โโโ user-service/
โโโ order-service/
โโโ payment-service/
โโโ gateway/
Python (Microservicios)¶
myapp-microservices/
โโโ services/
โ โโโ user-service/
โ โ โโโ app/
โ โ โ โโโ __init__.py
โ โ โ โโโ main.py
โ โ โ โโโ api/
โ โ โ โ โโโ __init__.py
โ โ โ โ โโโ v1/
โ โ โ โ โโโ __init__.py
โ โ โ โ โโโ users.py
โ โ โ โโโ domain/
โ โ โ โ โโโ __init__.py
โ โ โ โ โโโ entities/
โ โ โ โ โโโ events/
โ โ โ โ โโโ services/
โ โ โ โโโ infrastructure/
โ โ โ โ โโโ __init__.py
โ โ โ โ โโโ database/
โ โ โ โ โโโ messaging/
โ โ โ โ โโโ external/
โ โ โ โโโ application/
โ โ โ โโโ __init__.py
โ โ โ โโโ commands/
โ โ โ โโโ queries/
โ โ โ โโโ handlers/
โ โ โโโ requirements.txt
โ โ โโโ Dockerfile
โ โ โโโ tests/
โ โโโ order-service/
โ โ โโโ app/
โ โ โโโ requirements.txt
โ โ โโโ Dockerfile
โ โ โโโ tests/
โ โโโ payment-service/
โ โ โโโ app/
โ โ โโโ requirements.txt
โ โ โโโ Dockerfile
โ โ โโโ tests/
โ โโโ notification-service/
โ โโโ app/
โ โโโ requirements.txt
โ โโโ Dockerfile
โ โโโ tests/
โโโ gateway/
โ โโโ app/
โ โ โโโ __init__.py
โ โ โโโ main.py
โ โ โโโ middleware/
โ โ โโโ routing/
โ โ โโโ auth/
โ โโโ requirements.txt
โ โโโ Dockerfile
โโโ shared/
โ โโโ events/
โ โโโ exceptions/
โ โโโ logging/
โ โโโ messaging/
โโโ docker-compose.yml
โโโ docker-compose.dev.yml
โโโ k8s/
โโโ user-service/
โโโ order-service/
โโโ payment-service/
โโโ gateway/
โ Mejores Prรกcticas Microservicios¶
Comunicaciรณn entre Servicios¶
// .NET - Cliente HTTP para comunicaciรณn entre servicios
public class OrderServiceClient : IOrderServiceClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<OrderServiceClient> _logger;
private readonly OrderServiceOptions _options;
public OrderServiceClient(
HttpClient httpClient,
IOptions<OrderServiceOptions> options,
ILogger<OrderServiceClient> logger)
{
_httpClient = httpClient;
_options = options.Value;
_logger = logger;
}
public async Task<OrderDto?> GetOrderAsync(int orderId, CancellationToken cancellationToken = default)
{
try
{
var response = await _httpClient.GetAsync($"/api/orders/{orderId}", cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync(cancellationToken);
return JsonSerializer.Deserialize<OrderDto>(content, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Error calling Order Service for order {OrderId}", orderId);
throw new ServiceCommunicationException("Order Service", ex);
}
}
public async Task<bool> CreateOrderAsync(CreateOrderRequest request, CancellationToken cancellationToken = default)
{
try
{
var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/api/orders", content, cancellationToken);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Error creating order in Order Service");
throw new ServiceCommunicationException("Order Service", ex);
}
}
}
API Gateway con Ocelot (.NET)¶
// ocelot.json
{
"Routes": [
{
"DownstreamPathTemplate": "/api/users/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "user-service",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/users/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": []
},
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 60,
"Limit": 100
}
},
{
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "order-service",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/orders/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000",
"RateLimitOptions": {
"DisableRateLimitHeaders": false,
"QuotaExceededMessage": "Rate limit exceeded",
"HttpStatusCode": 429
}
}
}
Event-Driven Communication¶
// Evento de integraciรณn entre microservicios
public record UserRegisteredIntegrationEvent : IIntegrationEvent
{
public Guid UserId { get; init; }
public string Email { get; init; } = string.Empty;
public string FullName { get; init; } = string.Empty;
public DateTime RegistrationDate { get; init; }
public Guid EventId { get; init; } = Guid.NewGuid();
public DateTime OccurredAt { get; init; } = DateTime.UtcNow;
}
// Handler en el servicio de notificaciones
public class UserRegisteredIntegrationEventHandler : IIntegrationEventHandler<UserRegisteredIntegrationEvent>
{
private readonly INotificationService _notificationService;
private readonly ILogger<UserRegisteredIntegrationEventHandler> _logger;
public UserRegisteredIntegrationEventHandler(
INotificationService notificationService,
ILogger<UserRegisteredIntegrationEventHandler> logger)
{
_notificationService = notificationService;
_logger = logger;
}
public async Task Handle(UserRegisteredIntegrationEvent @event)
{
try
{
// Enviar email de bienvenida
await _notificationService.SendWelcomeEmailAsync(
@event.Email,
@event.FullName);
// Crear notificaciรณn en la app
await _notificationService.CreateInAppNotificationAsync(
@event.UserId,
"ยกBienvenido a nuestra plataforma!",
"Tu cuenta ha sido creada exitosamente.");
_logger.LogInformation(
"Welcome notifications sent for user {UserId}",
@event.UserId);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Failed to send welcome notifications for user {UserId}",
@event.UserId);
throw;
}
}
}
Docker Compose para Desarrollo¶
# docker-compose.yml
version: '3.8'
services:
# API Gateway
gateway:
build:
context: ./src/ApiGateways/MyApp.Gateway.Web
dockerfile: Dockerfile
ports:
- "5000:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
- user-service
- order-service
- payment-service
networks:
- myapp-network
# User Service
user-service:
build:
context: ./src/Services/User
dockerfile: MyApp.User.API/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Server=user-db;Database=UserDb;User=sa;Password=YourPassword123!;TrustServerCertificate=true
- EventBus__Connection=amqp://guest:guest@rabbitmq:5672
depends_on:
- user-db
- rabbitmq
networks:
- myapp-network
user-db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123!
volumes:
- user-db-data:/var/opt/mssql
networks:
- myapp-network
# Order Service
order-service:
build:
context: ./src/Services/Order
dockerfile: MyApp.Order.API/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Server=order-db;Database=OrderDb;User=sa;Password=YourPassword123!;TrustServerCertificate=true
- EventBus__Connection=amqp://guest:guest@rabbitmq:5672
- ExternalServices__UserService=http://user-service
depends_on:
- order-db
- rabbitmq
networks:
- myapp-network
order-db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123!
volumes:
- order-db-data:/var/opt/mssql
networks:
- myapp-network
# Payment Service
payment-service:
build:
context: ./src/Services/Payment
dockerfile: MyApp.Payment.API/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Server=payment-db;Database=PaymentDb;User=sa;Password=YourPassword123!;TrustServerCertificate=true
- EventBus__Connection=amqp://guest:guest@rabbitmq:5672
depends_on:
- payment-db
- rabbitmq
networks:
- myapp-network
payment-db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123!
volumes:
- payment-db-data:/var/opt/mssql
networks:
- myapp-network
# Message Broker
rabbitmq:
image: rabbitmq:3-management
ports:
- "15672:15672" # Management UI
- "5672:5672" # AMQP port
environment:
- RABBITMQ_DEFAULT_USER=guest
- RABBITMQ_DEFAULT_PASS=guest
volumes:
- rabbitmq-data:/var/lib/rabbitmq
networks:
- myapp-network
# Monitoring
seq:
image: datalust/seq:latest
ports:
- "5341:80"
environment:
- ACCEPT_EULA=Y
volumes:
- seq-data:/data
networks:
- myapp-network
volumes:
user-db-data:
order-db-data:
payment-db-data:
rabbitmq-data:
seq-data:
networks:
myapp-network:
driver: bridge
๐ Guรญa de Selecciรณn de Patrones¶
๐ฏ Matriz de Decisiรณn¶
| Criterio | MVC | Hexagonal | Clean | Capas | CQRS | Event-Driven | Microservicios |
|---|---|---|---|---|---|---|---|
| Complejidad del Proyecto | Baja | Media-Alta | Alta | Media | Alta | Alta | Muy Alta |
| Tamaรฑo del Equipo | Pequeรฑo | Medio | Grande | Medio | Grande | Grande | Muy Grande |
| Requisitos de Escalabilidad | Baja | Media | Alta | Media | Muy Alta | Alta | Muy Alta |
| Independencia de Tecnologรญa | Baja | Alta | Muy Alta | Media | Media | Media | Muy Alta |
| Facilidad de Testing | Media | Alta | Muy Alta | Media | Alta | Media | Alta |
| Curva de Aprendizaje | Baja | Media | Alta | Baja | Alta | Alta | Muy Alta |
| Mantenibilidad a Largo Plazo | Media | Alta | Muy Alta | Media | Alta | Alta | Alta |
๐ Recomendaciones por Escenario¶
Proyectos Pequeรฑos/Medianos (1-5 desarrolladores)¶
- Recomendado: MVC o Arquitectura por Capas
- Razรณn: Simplicidad, rapidez de desarrollo, menor overhead
Proyectos Empresariales (5-15 desarrolladores)¶
- Recomendado: Clean Architecture o Arquitectura Hexagonal
- Razรณn: Mejor separaciรณn de responsabilidades, alta testabilidad
Sistemas de Alta Escala (15+ desarrolladores)¶
- Recomendado: Microservicios + CQRS + Event-Driven
- Razรณn: Escalabilidad independiente, equipos autรณnomos
Sistemas con Requisitos de Performance Extrema¶
- Recomendado: CQRS + Event-Driven
- Razรณn: Optimizaciรณn independiente de lectura/escritura
๐ก Consejos de Implementaciรณn¶
- Comienza Simple: Inicia con MVC y evoluciona segรบn las necesidades
- No Sobre-Ingenierices: Usa la arquitectura mรกs simple que resuelva tus problemas
- Considera el Contexto: Evalรบa el tamaรฑo del equipo, experiencia y requisitos
- Evoluciรณn Gradual: Puedes migrar de una arquitectura a otra gradualmente
- Documentaciรณn: Mantรฉn documentada la arquitectura elegida y las razones
๐ก Recuerda: No existe una arquitectura perfecta para todos los casos. La mejor arquitectura es la que mejor se adapta a tu contexto especรญfico: tamaรฑo del equipo, complejidad del dominio, requisitos de escalabilidad y restricciones tecnolรณgicas.