Saltar a contenido

๐Ÿ—๏ธ 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

  1. MVC (Model-View-Controller)
  2. Arquitectura Hexagonal (Ports & Adapters)
  3. Clean Architecture
  4. Arquitectura por Capas (Layered)
  5. CQRS (Command Query Responsibility Segregation)
  6. Event-Driven Architecture
  7. 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

  1. Comienza Simple: Inicia con MVC y evoluciona segรบn las necesidades
  2. No Sobre-Ingenierices: Usa la arquitectura mรกs simple que resuelva tus problemas
  3. Considera el Contexto: Evalรบa el tamaรฑo del equipo, experiencia y requisitos
  4. Evoluciรณn Gradual: Puedes migrar de una arquitectura a otra gradualmente
  5. 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.