Second workshop of a series where the goal is to refactor a legacy application towards a microservices designed system.
In this workshop we explain concepts such as Commands, Domain Events, Use Cases, and see patterns to model and test them.
1. II - Vitaminando nuestros casos de uso
Maikel González Baile
Software Engineer @Ontruck
@mgonzalezbaile
Luis Matesanz Barroso
CTO @Gstock
@sitocasla
#phpmadusecases
2. ¿Por qué no una charla al uso?
● Charlas teóricas pero poco debate en posibles implementaciones.
● Mob programming, live coding, kata ...
● Serie de workshops para intentar llegar desde una aplicación con código
legacy a una aplicación distribuida y escalable.
3. Code Review Marketplace
Hemos creado un Marketplace en el que un
usuario puede crear una Pull Request y solicitar
su revisión a cualquier revisor registrado en la
plataforma.
El sistema calculará la cuota que el creador de
la PR debe pagar para ver las revisiones.
Casos de uso:
● Crear Pull Request:
○ Código, Revisores, Fecha de Revisión
● Calcular cuota
○ #LOC, Fecha de Revisión, #Revisores
● Enviar notificación a revisores
● Aprobar Pull Request
4. Caso de Uso - ¿Qué es?
“In software and systems engineering, a
use case is a list of actions or event
steps typically defining the interactions
between a role (known in the Unified
Modeling Language (UML) as an actor)
and a system to achieve a goal. The actor
can be a human or other external
system.” - Wikipedia
Action: Edit an article
Actor: Member
Preconditions:
● The article has editing flag enabled.
● The article exists.
● The member exists.
Postconditions
● Success:
○ The article is edited.
● Failure:
○ Editing the article failed due to editing flag
was disabled.
5. Conceptos explícitos en el código
● Action (intención de hacer algo) == Command
● Pre-conditions (reglas de negocio, invariantes, …) == Use Case
● Post-conditions (qué ha ocurrido) == Eventos de Dominio
● Policy (acción-reacción) == Pub/Sub + SAGAs
CreateUserCommand
● name
● address
● ...
CreateUserUseCase
if name < 5: ...
if userAlreadyExist: ...
CreateUserFailed
“Because user already
exists”
UserCreated
● name
● address
● ...
Policy
When: UserCreated
Then: SendEmailCommand
6. Ejemplos
● Approve Pull Request (Happy Path):
○ Given a Pull Request with some reviewers assigned.
○ When an assigned reviewer attempts to approve the Pull Request
○ Then the Pull Request should be approved.
● Approve Pull Request (Failure):
○ Given a Pull Request with some reviewers assigned.
○ When an unassigned reviewer attempts to approve the Pull Request
○ Then the approval should fail.
● Approve Pull Request (Failure):
○ Given a Pull Request already approved.
○ When an assigned reviewer attempts to approve the Pull Request
○ Then the approval should fail.
7. WTF?
● Pero... ¿y ésto cómo se testea?
● Sí, muy bien, retornamos un evento… ¿pero y la BBDD? No
hemos guardado nada!
● Genial! Ya tengo mi caso de uso! ¿Y ahora cómo lo ejecuto?
● Bueno, ¿y todo esto para qué?
● ¿Y ésto de dónde os lo habéis sacado?
8. ¿Ésto cómo se testea?
● Approve Pull Request (Happy Path):
○ Given a Pull Request with some reviewers assigned.
○ When an assigned reviewer attempts to approve the Pull Request
○ Then the Pull Request should be approved.
● Approve Pull Request (Failure):
○ Given a Pull Request with some reviewers assigned.
○ When an unassigned reviewer attempts to approve the Pull Request
○ Then the approval should fail.
● Approve Pull Request (Failure):
○ Given a Pull Request already approved.
○ When an assigned reviewer attempts to approve the Pull Request
○ Then the approval should fail.
9. CreatePrCommand
● code
● creator
● ...
¿Y la BDD?
CreatePrUseCase
if code < 5: ...
PrCreated
● code
● creator
● ...
PullRequest
● code
● creator
● ...
● Nos aseguramos que todos los datos que queramos tener en nuestra tabla deben
estar en el evento, creando un log de eventos consistente con el estado de nuestras
tablas.
● Separación total de Dominio e Infraestructura a la hora de lidiar con un problema u
otro.
● Aislamiento de la lógica de negocio y persistencia a la hora de testear nuestro
código.
● Modificar y/o extender nuestro esquema de BBDD no tiene porqué impactar nuestro
PullRequestCreated
PullRequestApproved
PullRequestMerged
CollectMoneyFailed
EVENT STORE
10. Test de Integración
● Approve Pull Request (Happy Path):
○ Given a Pull Request with some reviewers assigned.
○ When an assigned reviewer attempts to approve the Pull Request
○ Then the Pull Request should be approved.
○ And the projection should be XXX.
13. ¿Y todo ésto para qué?
● Legibilidad del código:
○ Given/When/Then
○ Conceptos de dominio explícitos
● Eventos:
○ Métricas de negocio
○ Audit log
○ Errores => Excepciones => Bugs
○ Pub/Sub
■ Desacoplamiento
■ OCP & SRP (SOLID)
● Divide y Vencerás:
○ Foco en negocio o infraestructura
○ Paralelismo de tareas: caso de uso,
persistencia, API (+ doc)
● Robustez for free:
○ Transacción
○ DB Locks acotados
○ Consistencia estado + eventos
○ Trazabilidad
○ Tests dependientes de
comportamiento y no de estructura
● Homogeneidad de código cross
language
● Framework de negocio
Mostrar cómo estaba el código y cómo quedó tras la refactorización: Lógica de negocio desperdigada por Entidad, Controller, DoctrineListener, …. => Casos de uso
No tests: Tests para cada caso de uso y End2End
Punto de vista de Backend, no UI.
No estamos hablando de Infraestructura, BBDD, APIs, Cron jobs, ….
Beneficios caso de uso:
Al ser una única clase es difícil, a menos que estés modificando diferentes funcionalidades a la vez, tener conflictos.
Te permite enfocarte en un determinado problema/funcionalidad, “olvidando el resto”
Al estar separado el comando es fácilmente serializable para automatizar la llamada del caso de uso o ser persistido en BBDD.
Mencionar la historia que tiene detrás el CommandHandler para ver cómo ha evolucionado del Command pattern, pasando por los Application Services hasta él.
Command - Handler: https://jimmybogard.com/domain-command-patterns-handlers/
Evento en past tense
Seguimos queriendo trasladar el lenguaje de negocio, humano, a nuestro código.
Test de comportamiento, no de estructura -> mover lógica del caso de uso a la Entidad
Dónde y cómo persistir el evento y la Entidad ES OTRO PROBLEMA.
Mostrar ejemplo de endpoint
Estos 3 patrones nos permiten extender la funcionalidad de nuestros casos de uso de manera transparente para ellos