Skip to content

Instantly share code, notes, and snippets.

@danilat

danilat/blog.md Secret

Created July 29, 2018 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danilat/23c4c8e14eb61e39c7d20430beebc3b8 to your computer and use it in GitHub Desktop.
Save danilat/23c4c8e14eb61e39c7d20430beebc3b8 to your computer and use it in GitHub Desktop.

Arquitectura

La aplicación ha desarrollada utilizando patrones tácticos de Domain Driven Design. De fuera hacia adentro: Application Services, Domain Services, Repositories y Aggregates formados por Entities y Value Objects. De modo que combinando esto con una aproximación de ports & adapters (arquitectura hexagonal), la lógica de negocio está completamente desacoplada de la infraestructura, que son detalles de implementación. El framework web utilizado es Slim y para la persistencia se utiliza PDO, hay algunas otras librerías con menor impacto. Al utilizar la aproximación de ports & adapters, se ha evitado la dependencia a cualquiera de esas librerías más allá de los adapters o el uso desde Slim del core de negocio. El sistema de directorios está organizado de este modo:

  • src
  • db
  • templates-
  • tests
  • public.

src

Como es de suponer, en src tenemos el grueso de lo que nos interesa, dentro de ese directorio tenemos la siguiente estructura: Actions. Que son los Application Services, puntos de entrada al sistema de cómo se interactúa con él y orquesadores con el mundo exterior. Están organizados por unidad lógica con el nombre en plural: Consents / LegalDocuments / Sites / Templates. Y los nombres de las clases son (o deberían ser) autoexplicativos. Hay definido un BaseAction, ahora mismo vacíos, con intención de decorar las acciones para cuestiones como la auditoría y la transaccionalidad de lo que ocurre dentro de cada acción; temas que están en el TODO. Podréis encontrar algunas diferencias entre acciones pertenecientes a Write Model y Read Model. Las de Write Model trabaja puramente con repositorios, entidades, etc. ya que es donde ocurre el dominio. Mientras que en algunas de Read Model devolvemos instancias de clases con el "apellido" Projection, que simplemente son DTOs inmutables que devuelven una proyección del estado del dominio. Actualmente se componen a través de los datos traídos de los repositories, lo cuál no es óptimo por el antipatrón de queries N+1, así que se podría modificar eso pronto para hacer joins; tal vez con una abstracción con algún apellido tipo Query para dotarle de cambiabilidad (una vista, otra base de datos con otro esquema o tecnología…) sin tener que tocar el Action. Infrastructure. Es el mundo exterior, lo que no podemos testear de forma unitaria: el tiempo del sistema, un generador de ids, utilizades para trabajar con el dom… también podrían haber caído aquí las implementaciones concretas de las interfaces de los patrones Repository, seguramente sea mejor sitio que en el Model. Model: Es el core formado por las reglas de negocio y entidades de dominio. Están organizados con el nombre en singular: Consent / LegalDocument / Site / Template. Como se puede comprobar, las entidades no mapean 1:1 a la persistencia relacional como sería utilizando un ORM, si no que en las implementaciones de los Repository hacemos el trabajo de mapeo con la persistencia. Evidentemente podrían implementarse otros Repository con cualquier ORM, o base de datos no relacional. App.php: Es el framework Slim. Es un fichero bastante grande (más e 300 líneas) al tener mezclada las responsabilidades de la inyección de dependencias y la definición de las rutas y su mapeo con sus respectivas Actions. Propongo partir ambas responsabilidades, e incluso partir la definición de las rutas de zonas público y privada.

db

En db tenemos las migraciones de la base de datos, esto es que es lo que tenemos versionado del esquema de la base de datos, facilitando su sincronización y puesta al día. No es que resuelva todos los problemas, pero es un paso que puede ahorrar muchos dolores de cabeza. En este caso se está utilizando phinx.

templates

En el directorio templates tenemos las plantillas html de la UI, como tal vez pueda confundir se me ocurre renombrarlo a views o algo así, por no mezclarlo con el concepto de Template del lenguaje ubicuo. Esas plantillas apenas tienen html, por no generar código tontamente que luego pueda molestar para meterle el diseño final.

tests

En tests está replicada la misma estructura que en src: Actions / Infrastructure / Model. En Actions tenemos una mayoría de tests unitarios de las acciones, de modo que se testea desde ahí las interacciones y reglas de negocio. Aunque para algunas acciones de Read Model son tests de integración, ya que así no tenemos tanto acoplamiento teniendo que doblar a sus colaboradores, son algo más lentos pero en esos casos vi que no aportaba demasiado todo ese andamiaje que no aporta demasiado. En Infrastructure son todo tests de integración, como se puede ver en la implementación son tests muy generales que prueban sólo casos que nos den seguridad comprobando las salidas. En Model tenemos los tests de los Repository, que son de integración también. De estos comprobamos un contrato, para unas entradas esperamos unas salidas. De modo que aún cambiando la implementación de la tecnología de persistencia esos tests nos deberían valer, sólo deberíamos cambiar lo relacionado con el setup del system under test En AppTest.php comprobamos a nivel de integración con framework el happy path y algunos casos de error con llamadas http a las rutas. Así tenemos bastante seguridad de que las piezas juntas funcionan a través de la inyección de dependencias, además de las referencias a las plantillas html. Esos tests automáticos son los de más alto nivel de la pirámide de testing, no hay tests end 2 end desde un navegador.

public

En public simplemente están los assets y ficheros php que inicializan la aplicación en los diferentes entornos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment