Arquitectura y estructura de carpetas - MiguelAngelMarinMejia1/ArquitecturaDeSoftware-Proyecto GitHub Wiki
VisiΓ³n general
Hotel MDJ estΓ‘ organizado en capas concΓ©ntricas: el nΓΊcleo de negocio no conoce a quien lo consume. Sobre ese nΓΊcleo se apoyan dos capas de presentaciΓ³n independientes βel sitio web renderizado en servidor y la API REST en JSONβ que comparten exactamente la misma lΓ³gica.
ββββββββββββββββββββββββββββββββββββββββββββββββ
β PRESENTACIΓN β
β ββββββββββββββββββ ββββββββββββββββββββ β
β β app web β β reservation.api β β
β β HTML + Django β β DRF / JSON β β
β ββββββββββ¬ββββββββ ββββββββββ¬ββββββββββ β
βββββββββββββΌββββββββββββββββββββββΌββββββββββββββ
β β
ββββββββββββ¬βββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β APLICACIΓN β
β services.py selectors.py β
β (comandos) (consultas) β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β DOMINIO β
β builders.py policies.py codes.py β
β exceptions.py β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β PERSISTENCIA INFRAESTRUCTURA β
β models.py infra/factories.py β
β infra/notifications β
β infra/payments.py β
ββββββββββββββββββββββββββββββββββββββββββββββββ
La regla que sostiene el diseΓ±o: las dependencias apuntan siempre hacia adentro. web/ importa
de reservation, pero reservation jamΓ‘s importa de web. Y dentro de reservation, solo el
subpaquete api/ conoce Django Rest Framework.
Estructura
hotelMDJProject/
βββ manage.py
βββ requirements.txt
βββ hotelMDJ/ # configuraciΓ³n del proyecto
β βββ settings.py
β βββ urls.py # '' β web | 'api/v1/' β reservation.api
β
βββ reservation/ # NΓCLEO DE NEGOCIO
β βββ models.py # entidades de persistencia
β βββ services.py # orquestaciΓ³n de casos de uso
β βββ selectors.py # consultas de lectura
β βββ admin.py
β βββ domain/
β β βββ builders.py # ReservationBuilder
β β βββ policies.py # PricingPolicy, TaxPolicy, CapacityPolicy
β β βββ codes.py # ConfirmationCodeGenerator
β β βββ exceptions.py # errores de dominio
β βββ infra/
β β βββ notifications.py # BaseNotifier + implementaciones
β β βββ payments.py # BaseGateway + implementaciΓ³n simulada
β β βββ factories.py # NotifierFactory, PaymentGatewayFactory
β βββ api/
β β βββ serializers.py
β β βββ views.py # APIView
β β βββ urls.py
β β βββ exception_handler.py # dominio β HTTP
β βββ fixtures/seed.json
β βββ migrations/
β
βββ web/ # PRESENTACIΓN HTML
βββ views.py
βββ urls.py
βββ templates/web/
βββ static/web/
JustificaciΓ³n de cada decisiΓ³n
Por quΓ© dos apps y no una
La alternativa evidente era meter todo en reservation/. Se separΓ³ web/ por una razΓ³n concreta:
hace imposible, por construcciΓ³n, que la lΓ³gica de negocio se filtre a la capa HTML. Si las
plantillas y las vistas de pΓ‘gina vivieran junto a los servicios, nada impedirΓa que un dΓa alguien
calculara un precio dentro de una vista. Con dos apps, esa dependencia serΓa un import cruzado
visible en revisiΓ³n de cΓ³digo.
El beneficio secundario es que el nΓΊcleo queda listo para extraerse: si maΓ±ana el sitio web pasa a
ser una SPA, se borra web/ y reservation/ sigue funcionando sin tocar una lΓnea.
Por quΓ© domain/ separado de models.py
El enunciado prohΓbe lΓ³gica de negocio en mΓ©todos de los modelos. Pero el problema no es solo normativo: los modelos de Django estΓ‘n atados al ORM, y una regla de negocio escrita dentro de un modelo solo se puede probar levantando una base de datos.
En domain/ las reglas son clases planas de Python. PricingPolicy.quote() se prueba con objetos
en memoria, sin migraciones y sin base de datos.
Por quΓ© services.py y selectors.py separados
Los comandos (que cambian estado) y las consultas (que solo leen) tienen necesidades distintas: los
primeros necesitan transacciones, validaciones e invariantes; las segundas necesitan select_related
y filtros eficientes. Mezclarlos produce clases que crecen sin control.
selectors.py concentra ademΓ‘s toda la lΓ³gica de solapamiento de fechas, que es la consulta mΓ‘s
delicada del sistema y merece un ΓΊnico lugar donde razonarla.
Por quΓ© infra/ aislado
notifications.py y payments.py son adaptadores a mundos externos (SMTP, una pasarela de pago).
Son la parte del sistema con mΓ‘s probabilidad de cambiar por razones ajenas al negocio. Aislarlos
detrΓ‘s de las abstracciones BaseNotifier y BaseGateway significa que cambiar de proveedor de
correo no toca ni un archivo de domain/ ni de services.py.
Por quΓ© api/ es un subpaquete y no un app
La API no es un contexto de negocio distinto: es otra forma de exponer el mismo. Ponerla como
subpaquete deja explΓcito que depende de reservation, y concentra en un solo directorio todo lo
que sabe de HTTP y de DRF.
Mapeo entre el diagrama de clases y el cΓ³digo
El diagrama UML declara operaciones dentro de las entidades. Al implementarlas se reubicaron en la capa que les corresponde, manteniendo la semΓ‘ntica:
| OperaciΓ³n del diagrama | ImplementaciΓ³n |
|---|---|
Reserva.calcularTotal() |
domain/policies.py β PricingPolicy.quote() |
Reserva.confirmar() / cancelar() |
services.py β ReservationService |
Reserva.modificarFechas() |
Entrega 2 |
Habitacion.verificarDisponibilidad() |
selectors.py β available_rooms(), is_room_available() |
Habitacion.cambiarEstado() |
models.Room.Status + admin |
TipoHabitacion.calcularPrecioTemporada() |
domain/policies.py β SeasonalPricingPolicy |
Cliente.registrarse() |
services.py β CustomerService.get_or_create_profile() |
Cliente.consultarHistorialReservas() |
reservation.customer.reservations (related name) |
Pago.procesarPago() |
services.py β PaymentService.charge() |
Factura.generarFactura() |
services.py β InvoiceService.issue() |
Modelo de datos implementado
Se implementaron las seis clases del diagrama mΓ‘s una tabla intermedia:
| Clase | Estado |
|---|---|
Customer |
Completa, con flujo de negocio y API |
RoomType |
Completa, con flujo de negocio y API |
Room |
Completa, con flujo de negocio y API |
Reservation |
Completa, con flujo de negocio y API |
ReservationRoom |
Tabla intermedia que materializa el 1..* del diagrama |
Payment |
Persistencia + PaymentService con pasarela simulada |
Invoice |
Persistencia + InvoiceService |
ReservationRoom no aparece en el diagrama porque UML expresa la asociaciΓ³n 1..* directamente
entre Reserva y Habitacion. En un modelo relacional esa asociaciΓ³n necesita una tabla propia, y
ademΓ‘s da un lugar natural donde congelar el precio por noche en el momento de reservar: si la
tarifa cambia despuΓ©s, lo que el huΓ©sped aceptΓ³ pagar no cambia con ella.