|
| 1 | +# Architecture Guide |
| 2 | + |
| 3 | +This repo uses two architectural levels on purpose: |
| 4 | + |
| 5 | +- a simple layered flow for the student example |
| 6 | +- a more explicit ports-and-adapters flow for the course example |
| 7 | + |
| 8 | +That split is intentional for learning. You can start with the simpler version and then see how a more maintainable design looks when persistence and security become more important. |
| 9 | + |
| 10 | +## High-level structure |
| 11 | + |
| 12 | +```text |
| 13 | +HTTP request |
| 14 | + -> controller |
| 15 | + -> use-case interface |
| 16 | + -> application service |
| 17 | + -> store interface |
| 18 | + -> Mongo adapter |
| 19 | + -> Spring Data repository |
| 20 | + -> MongoDB |
| 21 | +``` |
| 22 | + |
| 23 | +For responses, the flow goes back in the opposite direction, with the controller mapping domain objects into response DTOs. |
| 24 | + |
| 25 | +## Why the course module is more structured |
| 26 | + |
| 27 | +The student flow is intentionally small and direct so beginners can learn controller/service/repository basics first. |
| 28 | + |
| 29 | +The course flow uses stronger boundaries because it is the better example for long-term maintainability: |
| 30 | + |
| 31 | +- the controller should not know MongoDB details |
| 32 | +- the application service should not depend on web DTOs |
| 33 | +- the domain model should not depend on Spring Data classes |
| 34 | +- persistence-specific mapping should live in the adapter layer |
| 35 | + |
| 36 | +This keeps each layer focused on one job. |
| 37 | + |
| 38 | +## Main patterns used |
| 39 | + |
| 40 | +### Layered architecture |
| 41 | + |
| 42 | +The repo still follows a familiar layered shape: |
| 43 | + |
| 44 | +- controller layer |
| 45 | +- service/application layer |
| 46 | +- persistence layer |
| 47 | + |
| 48 | +That makes the code easy to navigate. |
| 49 | + |
| 50 | +### Dependency inversion |
| 51 | + |
| 52 | +The application service depends on `CourseStore`, not on `MongoRepository`. |
| 53 | + |
| 54 | +That means the core use case depends on an interface, and the Mongo adapter implements it. This is the main architectural improvement in the repo. |
| 55 | + |
| 56 | +### Ports and adapters |
| 57 | + |
| 58 | +The course module uses a lightweight hexagonal style: |
| 59 | + |
| 60 | +- input port: `CourseService` |
| 61 | +- output port: `CourseStore` |
| 62 | +- adapter: `MongoCourseStore` |
| 63 | + |
| 64 | +This is useful because it makes framework code replaceable instead of central to the core logic. |
| 65 | + |
| 66 | +### Repository pattern |
| 67 | + |
| 68 | +`CourseRepository` is the Spring Data repository for MongoDB. |
| 69 | + |
| 70 | +It is not used directly by the controller or application service anymore. It sits behind `MongoCourseStore`. |
| 71 | + |
| 72 | +### Mapper pattern |
| 73 | + |
| 74 | +There are two kinds of translation in the course module: |
| 75 | + |
| 76 | +- controller maps request DTOs into application commands and domain objects into response DTOs |
| 77 | +- `CourseDocumentMapper` maps domain objects into Mongo documents and back |
| 78 | + |
| 79 | +This avoids leaking transport or database concerns across layers. |
| 80 | + |
| 81 | +## Package roles in the course module |
| 82 | + |
| 83 | +- `spring.persistence.domain` |
| 84 | + - framework-independent domain model |
| 85 | +- `spring.persistence.service` |
| 86 | + - input port and application service |
| 87 | +- `spring.persistence.store` |
| 88 | + - output port and Mongo adapter |
| 89 | +- `spring.persistence.document` |
| 90 | + - MongoDB document classes |
| 91 | +- `spring.persistence.repository` |
| 92 | + - Spring Data repository interfaces |
| 93 | +- `spring.persistence.controller` |
| 94 | + - HTTP entry points and request/response mapping |
| 95 | + |
| 96 | +## Why this is better than the previous version |
| 97 | + |
| 98 | +Before the refactor: |
| 99 | + |
| 100 | +- the service knew about request DTOs |
| 101 | +- the service returned response DTOs |
| 102 | +- the service depended directly on the Mongo repository |
| 103 | + |
| 104 | +After the refactor: |
| 105 | + |
| 106 | +- the controller owns HTTP DTO mapping |
| 107 | +- the service owns use-case logic |
| 108 | +- the store adapter owns persistence translation |
| 109 | +- the domain model stays free of Spring and Mongo classes |
| 110 | + |
| 111 | +That is a cleaner separation of responsibilities. |
| 112 | + |
| 113 | +## Tradeoff |
| 114 | + |
| 115 | +This architecture adds more files. |
| 116 | + |
| 117 | +That is the cost of clearer boundaries. |
| 118 | + |
| 119 | +For a tiny project, that would sometimes be too much. For a learning repo, it is useful because it shows what "better architecture" actually looks like in code rather than only describing it in theory. |
0 commit comments