Proyecto Todo-List
En esta parte de desarrollo con enfoque outside-in, realizaremos un pequeño proyecto que consiste en una API para una aplicación de lista de tareas.
Queremos implementar las siguientes funcionalidades:
US 1
- As a User
- I want to add tasks to a to-do list
- So that, I can organize my task
US 2
- As a User
- I want to see the task in my to-do list
- So that, I can know what I have to do next
US 3
- As a User
- I want to check a task when it is done
- So that, I can see my progress
Ejemplos para tests
- Write a test that fails (done)
- Write Production code that makes the test pass
- Refactor if there is opportunity
Endpoints, payloads y respuestas
1 POST /api/todo
2 [task:Write a test that fails]
3 201. Created
4
5 POST /api/todo
6 [task:Write Production code that makes the test pass]
7 201. Created
8
9 POST /api/todo
10 [task:Refactor if there is opportunity]
11 201. Created
12
13 PATCH /api/todo/1
14 [done:true]
15 200. Ok
16
17 GET /api/todo
18 [√] 1. Write a test that fails
19 [ ] 2. Write Production code that makes the test pass
20 [ ] 3. Refactor if there is opportunity
21 200. Ok
Para simplificar, la lista de tareas que esperamos es un array de strings, con los datos de las tareas formateados.
Diseño
Para desarrollar outside-in es necesario hacer un cierto diseño previo. Por supuesto, no se trata de generar todas las especificaciones de los componentes hasta el mínimo detalle, sino de plantear una idea general del modelo de arquitectura que vamos a seguir y los grandes componentes que esperamos desarrollar.
Esto nos ayudará a ubicar los distintos elementos y comprender sus relaciones y dependencias. Nos proporciona un contexto de cómo funciona el ciclo de la aplicación y cómo se organizan y comunican sus componentes.
Capas
Nuestra aplicación se organizará en capas:
- Dominio: contiene las entidades del dominio que son el corazón mismo de la aplicación y en la que se representan los conceptos, procesos y reglas de negocio.
- Aplicación: los distintos casos de uso de la aplicación, representando las intenciones de sus consumidoras
-
Infraestructura: las implementaciones concretas necesarias para que la aplicación funciones. A su vez, esta capa tiene diversos puertos:
- Puntos de entrada, como puede ser el Api, que contiene los controladores que se encargan de interaccionar con las consumidoras. En su caso aquí también residirían los comandos de consola y otros.
- Persistencia: los adaptadores de las tecnologías de persistencia que necesitamos para implementar el repositorio.
- En caso necesario, otros adaptadores.
- Vendor o Lib, contienen los recursos de terceros que necesita la aplicación para funcionar.
Las dependencias apuntan siempre hacia el dominio.
Flujo de la aplicación
Al hacer una request HTTP a un endpoint, un controlador recoge los datos necesarios y los pasa a una instancia del caso de uso correspondiente. Recoge la respuesta, si la hay, y la transforma para entregarla a la consumidora.
El caso de uso instancia o reclama del repositorio las entidades de dominio que sean necesarias y utiliza los servicios de dominio para realizar su tarea.
Los casos de uso pueden adoptar la forma de comands o queries. En el primer caso, provocan un efecto en el sistema. En el segundo, devuelven una respuesta. Para acomodar la respuesta a la demanda del controlador, pueden usar algún tipo de transformador de datos, de modo que los objetos de dominio no llegan nunca al controlador, sino una representación. Mediante un patrón Strategy podemos hacer que el controlador decida en qué representación concreta está interesado.
Arquitectura
Construiremos la aplicación usando el enfoque de arquitectura hexagonal1 con una estructura de tres capas: dominio, aplicación e infrastructura, tal como hemos detallado un poco más arriba.El desarrollo comenzará con un test de aceptación, que actúa como consumidor de la API, lo que nos llevará a implementar los controladores, en primer lugar,
Este es un esquema genérico del tipo de arquitectura que tenemos en mente al desarrollar esta aplicación.