{"id":17133003,"url":"https://github.com/agraphie/modular_monolith","last_synced_at":"2026-04-24T11:36:43.084Z","repository":{"id":149590895,"uuid":"489421696","full_name":"Agraphie/modular_monolith","owner":"Agraphie","description":null,"archived":false,"fork":false,"pushed_at":"2022-12-23T13:31:53.000Z","size":76,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T05:44:22.380Z","etag":null,"topics":["microservices","monolith","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Agraphie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-06T16:23:57.000Z","updated_at":"2022-12-21T11:41:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"a057adea-2a5b-42eb-9456-57d47005f87d","html_url":"https://github.com/Agraphie/modular_monolith","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Agraphie/modular_monolith","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agraphie%2Fmodular_monolith","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agraphie%2Fmodular_monolith/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agraphie%2Fmodular_monolith/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agraphie%2Fmodular_monolith/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Agraphie","download_url":"https://codeload.github.com/Agraphie/modular_monolith/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agraphie%2Fmodular_monolith/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261296915,"owners_count":23137218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["microservices","monolith","spring-boot"],"created_at":"2024-10-14T19:29:05.997Z","updated_at":"2026-04-24T11:36:38.042Z","avatar_url":"https://github.com/Agraphie.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modular Monolith\n\nThis repository is aimed to be a somewhat realistic approach on how several teams can work in\none code-base. The goal is to apply some lessons learnt from a microservice architecture \nto a monolithic architecture.\nThe aim of this example project is to keep the architecture as simple as possible while achieving \na maximum of isolation between the code of the teams as well as the different modules.\nIn the end, it doesn't matter though if you go for a simple layered architecture or a more\nsophisticated onion architecture. This project setup as well as the rules will fit any.\n\nGradle is used to avoid any transitive dependencies and hide dependencies as much as possible.\nIn general, this setup can be replicated with any language and any build module which supports a \nstrong isolation and hiding of transitive dependencies.\n\n# Table of contents\n- [Rules for a modular monolith](#rules-for-a-modular-monolith)\n- [Use case for this example project](#use-case-for-this-example-project)\n  - [Setup](#setup)\n    - [High-level overview](#high-level-overview)\n    - [Dependency definitions](#dependency-definitions)\n    - [Keeping it simple](#keeping-it-simple)\n- [FAQ](#faq)\n- [Run the project](#run-the-project)\n  - [Intellij Idea](#intellij-idea)\n  - [Gradle](#gradle)\n\n\n## Rules for a modular monolith\n- Every outgoing connection needs to be wrapped in circuit breakers[^1] and fallbacks. Teams agree\non SLAs and fallbacks\n- Every outgoing call needs to go through some defined adapters. We'll borrow this concept from\nthe ports and adapters architecture[^2]\n- Every module defines its own dependencies and configs and keeps them as encapsulated as possible\n- Every module has a clear owner. Other teams can participate, but their code needs to be isolated\n- Every other team is treated as foreign code and service, as if it was another microservice\n- Dependencies in a module should never be transitively exposed to other modules\n- View dependencies as part of your responsibility and think about them actively\n- Classes should be package private by default. One could e.g. cut the packages by domain or by feature\n\n## Use case for this example project\nThe exemplary use case for this repository is there are two teams contributing to this application.\nThe \"app\" team as well the \"ai\" team. While the \"app\" team owns this code and both services, the\n\"ai\" team should be able to contribute to this code to enhance certain features from their perspective.\nThe agreement is to use Spring Boot, but the used dependencies should be flexible per team and per deployable.\n\nFurther, the user-facing deployable makes use of prepared data from the internal deployable. Meaning,\nthe internal deployable receives data and transforms and enhances it for the user-facing deployable to use.\n\n### Setup\n\n#### High-level overview\n- The \"app\" team owns this repository as well as the internal ([indexation](indexation)) and \nexternal ([app](app)) deployable\n- The \"ai\" team contributes to these deployables by using certain ai features ([ai](ai)). This would include\n  calling their microservice(s) not in this repository as well as adding business logic \n- The internal deployed application follows an event-driven approach and shares the persisted entities\n  ([entities](indexation/entities)) with the user-facing services for convenience\n- The external deployed service follows a classic non-reactive layered architecture approach\n\n#### Dependency definitions\n- The dependencies defined in the respective gradle files in [buildSrc/src/main/kotlin](buildSrc/src/main/kotlin)\nshould be carefully defined. Only absolutely necessary dependencies should be defined on global levels\n- We define Spring Boot Gradle plugin as well as the Spring Boot dependency management plugin \n on a global level in [buildSrc/src/main/kotlin/modular_monolith.java-common-conventions.gradle.kts](buildSrc/src/main/kotlin/modular_monolith.java-common-conventions.gradle.kts).\n**This definition makes not assumption about any used Spring modules (Web, WebFlux, Data, ...)!** \nThese dependencies should be defined in their respective modules\n- As we defined Spring as common framework, we define Spring Context as common dependency \nin [buildSrc/src/main/kotlin/modular_monolith.java-library-conventions.gradle.kts](buildSrc/src/main/kotlin/modular_monolith.java-library-conventions.gradle.kts)\nso every module can make easily use of Sprint annotations\n\n#### Keeping it simple\nFor the module [entities](indexation/entities) if we stick to a strict and correct isolation, we would have to\nduplicate the entities in both modules ([indexation](indexation)) and ([app](app)). While this is certainly the\nmore correct way, we decided to go for a more pragmatic approach. Depending on the complexity of the app, this\nshould not be done.\n\n## FAQ\n\u003e Where should I put common domain logic? \n\nYou could create a new module called \"domain\" and let all modules use it. The \"domain\" module should \n    not have any dependencies on any other module. To avoid the domain module becoming a dumping ground for\nall sorts of things (\"util\" package) we advise to think carefully whether it's needed.\n\u003cbr/\u003e\u003cbr/\u003e\n\n\u003e Why not common configuration module? \n\nEach deployable acts as independent service, as if they were microservices. In the end they might or\n  might not use the same data sources. We favour duplication over DRY here for better isolation.\u003cbr/\u003e\u003cbr/\u003e\n\n\u003e Why doesn't the indexation module have any circuit breakers?\n\nWith this example we wanted to show a rather simple and trivial module. If it was a more business critical\nand bigger module, it should absolutely have circuit breakers.\u003cbr/\u003e\u003cbr/\u003e\n\n\n\u003e Why explicitly scanning the base packages in the app module?\n\nWe could also put all the classes in the same package. With a monolith and using package private classes\nas default it makes sense though to give every team the biggest flexibility \nas well as hiding their classes.\u003cbr/\u003e\u003cbr/\u003e\n\n\u003e Why the Spring Boot plugin dependency in the common dependencies?\n\nAs it was decided for this example project to use Spring Boot, we can use this dependency\nto ensure a consistent version of all Spring Boot related dependencies in all modules.\u003cbr/\u003e\u003cbr/\u003e\n\n## Run the project\n### Intellij Idea\nIn intellij you might encounter an issue where Idea does not find all the split up config files. Run a gradle\nbuild in that case.\n\n### Gradle\n`./gradlew bootRun`\n\n[^1]: https://resilience4j.readme.io/\n[^2]: https://alistair.cockburn.us/hexagonal-architecture/","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagraphie%2Fmodular_monolith","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagraphie%2Fmodular_monolith","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagraphie%2Fmodular_monolith/lists"}