{"id":19385756,"url":"https://github.com/moduliths/moduliths","last_synced_at":"2025-04-23T22:32:05.039Z","repository":{"id":38737790,"uuid":"133571699","full_name":"moduliths/moduliths","owner":"moduliths","description":"Building modular, monolithic applications using Spring Boot","archived":true,"fork":false,"pushed_at":"2022-10-26T06:17:49.000Z","size":855,"stargazers_count":833,"open_issues_count":17,"forks_count":84,"subscribers_count":40,"default_branch":"main","last_synced_at":"2025-04-23T01:39:22.692Z","etag":null,"topics":["architecture","modularity","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moduliths.png","metadata":{"files":{"readme":"README.adoc","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}},"created_at":"2018-05-15T20:48:14.000Z","updated_at":"2025-04-21T05:59:08.000Z","dependencies_parsed_at":"2023-01-19T16:30:18.307Z","dependency_job_id":null,"html_url":"https://github.com/moduliths/moduliths","commit_stats":null,"previous_names":["odrotbohm/moduliths"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moduliths%2Fmoduliths","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moduliths%2Fmoduliths/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moduliths%2Fmoduliths/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moduliths%2Fmoduliths/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moduliths","download_url":"https://codeload.github.com/moduliths/moduliths/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250527146,"owners_count":21445312,"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":["architecture","modularity","spring-boot"],"created_at":"2024-11-10T10:02:57.712Z","updated_at":"2025-04-23T22:32:04.423Z","avatar_url":"https://github.com/moduliths.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= Moduliths\n\nIMPORTANT: The Moduliths project has been discontinued and been transferred to https://github.com/spring-projects-experimental/spring-modulith[Spring Modulith].\nThe current 1.3 release branch will see futher bugfix upgrades for as long as https://spring.io/projects/spring-boot#support[Spring Boot 2.7 is under open-source support].\nFor migration instructions, please consult the https://docs.spring.io/spring-modulith/docs/0.1.x/reference/html/#appendix.migrating-from-moduliths[Spring Modulith migration guide].\n\nA playground to build technology supporting the development of modular monolithic (modulithic) Java applications.\n\nimage:https://github.com/moduliths/moduliths/actions/workflows/build.yaml/badge.svg[\"Maven Build\", link=\"https://github.com/moduliths/moduliths/actions/workflows/build.yaml\"]\n\n== tl;dr\n\nModuliths is a Spring Boot extension based on ArchUnit to achieve the following goals:\n\n* _Verify modular structure between individual logical modules in monolithic Spring Boot applications._\n+\nPrevents cyclic dependencies as well as explicitly defined allowed dependencies to other modules.\nVerifies access to public components in API packages (convention based, customizable).\n* _Bootstrap a subset of the modules of a monolithic Spring Boot application._\n+\nLimits the bootstrap of Spring Boot (component scanning and application of auto-configuration) to a single module, a module plus its direct dependents or an entire tree.\n* _Derive PlantUML documentation about the modules._\n+\nTranslates the actual module structure into a PlantUML component diagram via Structurizr for easy inclusion in e.g. Asciidoctor based documentation. Diagrams can be rendered for a single module plus its collaborators or the entire system at once.\n\n[[quickstart]]\n== Quickstart\n\n1. Create a simple Spring Boot application (e.g. via Spring Initializer).\n2. Add the Moduliths dependencies to your project:\n+\n[source,xml]\n----\n\u003cdependencies\u003e\n\n  \u003c!-- For the @Modulith annotation --\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eorg.moduliths\u003c/groupId\u003e\n    \u003cartifactId\u003emoduliths-core\u003c/artifactId\u003e\n    \u003cversion\u003e${modulith.version}\u003c/version\u003e\n  \u003c/dependency\u003e\n\n  \u003c!-- Test support --\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eorg.moduliths\u003c/groupId\u003e\n    \u003cartifactId\u003emoduliths-test\u003c/artifactId\u003e\n    \u003cversion\u003e${modulith.version}\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n\n\u003c!-- If you use snapshots --\u003e\n\u003crepositories\u003e\n  \u003crepository\u003e\n    \u003cid\u003espring-snapshots\u003c/id\u003e\n    \u003curl\u003ehttps://repo.spring.io/libs-snapshot\u003c/url\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n----\n3. Setup your package structure like described \u003c\u003cmodules,here\u003e\u003e.\n4. Create a module test like described \u003c\u003cmodules.running-tests,here\u003e\u003e.\n\n[[context]]\n== Context\n\nWhen it comes to designing applications we currently deal with two architectural approaches: monolithic applications and microservices.\nWhile often presented as opposed approaches, in their extremes, they actually form the ends of a spectrum into which a particular application architecture can be positioned.\nThe trend towards smaller systems is strongly driven by the fact that monolithic applications tend to architecturally degrade over time, even if – at the beginning of their lives – an architecture is defined.\nArchitecture violations creep into the projects over time unnoticed. Evolvability suffers as systems become harder to change.\n\nMicroservices on the other hand promise stronger means of separation but at the same time introduce a lot of complexity as even for small applications teams have to deal with the challenges of distributed systems.\n\nThis repo acts as a playground to experiment with different approaches to allow defining modular monoliths, so that it's easy to maintain modularity over time and detect violations early.\nThis will keep the ability to modify and advance the codebase over time and ease the effort to split up the system in the attempt to extract parts of it into a dedicated project.\n\n[[the-architecture-code-gap]]\n=== The architecture-code-gap\n\nIn software projects, architectural design decisions and constraints are usually defined in some way and then have to be implemented in a code base.\nTraditionally the connection between the architectural decisions and the actual have been naming conventions that easily diverge and cause the architecture actually implemented in the code base to slowly degrade over time.\nWe'd like to explore stronger means of connections between architecture and code and even look into advanced support of frameworks and libraries to e.g. allow testability of individual components within an overall system.\n\nThere already exists a variety of technologies that attempts to bridge that gap from the architectural definition side, mostly by trying to capture the architectural definitions in executable form (see https://jqassistant.org/[jQAssistant] and \u003c\u003cexisting-tools\u003e\u003e) and verify whether the code base adheres to the conventions defined.\nIn this playground, we're going to explore the opposite way: providing conventions as well as library and framework means, to express architectural definitions directly inside the code base with two major goals:\n\n1. _**Getting the validation of the conventions closer to the code / developer**_ -- If architectural decisions are driven by the development team, it might feel more natural to define architectural concepts in the code base.\nThe more seamless an architectural rule validation system integrates with the codebase, the more likely it is that the system is used.\nAn architectural rule that can be verified by the compiler is preferred over a rule verified by executing a test, which in turn is preferred over a verification via dedicated build steps.\n2. _**Integration with existing tools**_ - Even in combination with existing tools, it might just help them to ship generic architectural rules out of the box with the developer just following conventions or explicitly annotating code to trigger the actual validation.\n\n[[design-goals]]\n== Design goals\n\n* Enable developers to write architecturally evident code, i.e. provide means to express architectural concepts in code to close the gap between the two.\n* Provide means to verify defined architectural constraints as close as possible to the code (by the compiler, through tests or additional build tools).\n* As little invasive as possible technology. I.e. we prefer documented conventions over annotations over required type dependencies.\n\n[[feature-set]]\n== Current feature set\n\n[[modules]]\n=== A module model for Java packages\n\n[[modules.simple]]\n==== The most simple module setup\n\nAt its very core, Modulith assumes you have your application centered around a single Java package (let's assume `com.acme.myapp`).\nThe application base package is defined by declaring a class that is equipped with the `@Modulith` annotation.\nIt's basically equivalent to `@SpringBootApplication` but indicates you're opting in into the module programming model and package structures.\n\n[NOTE]\n.Notation conventions\n====\n[source]\n----\n+ – public type\no – package protected type\n----\n====\n\nEvery direct sub-package of this package is considered to describe a module:\n\n[source]\n----\ncom.acme.myapp                          \u003c1\u003e\n+ @Modulith ….MyApplication\n\ncom.acme.myapp.moduleA                  \u003c2\u003e\n+ ….MyComponentA(MyComponentB)\n\ncom.acme.myapp.moduleB                  \u003c3\u003e\n+ ….MyComponentB(MySupportingComponent)\no ….MySupportingComponent\n\ncom.acme.myapp.moduleC                  \u003c4\u003e\n+ ….MyComponentC(MyComponentA)\n----\n\u003c1\u003e The application root package.\n\u003c2\u003e `moduleA`, implicitly depending on `moduleB`, only public components.\n\u003c3\u003e `moduleB`, not depending on other modules, hiding an internal component.\n\u003c4\u003e `moduleC`, depending on `moduleA` and thus `moduleB` in turn.\n\nIn this simple scenario, the only additional means of encapsulation is the Java package scope, that allows developers to hide internal components from other modules.\nThis is surprisingly simple and effective.\nFor more complex structural scenarios, see \u003c\u003cmodules.complex\u003e\u003e.\n\n[[modules.running-tests]]\n==== Running tests for a module\n\nAn individual module can be run for tests using the `@ModuleTest` annotation as follows:\n\n[source,java]\n----\npackage com.acme.myapp.moduleB;\n\n@RunWith(SpringRunner.class)\n@ModuleTest\npublic class ModuleBTest { … }\n----\n\nRunning the test like this will cause the root application class be considered as well as all explicit configuration inside it.\nThe test run will customize the configuration to limit the component scanning, the auto-configuration and entity scan packages to the package of the module test.\nIt will also verify dependencies between the modules.\nSee more on that in \u003c\u003cmodules.complex\u003e\u003e.\n\nFor `moduleB` this is very simple as it doesn't depend on any other modules in the application.\n\n===== Handling module dependencies in tests\n\nWithout any further configuration, running an integration test for a module that depends on other modules, will cause the `ApplicationContext` to start to fail as Spring beans depended on are not available.\nOne option to resolve this is to declare ``@MockBean``s for all dependencies required:\n\n[source, java]\n----\npackage com.acme.myapp.moduleA;\n\n@RunWith(SpringRunner.class)\n@ModuleTest\npublic class ModuleATest {\n\n  @MockBean MyComponentB myComponentB;\n}\n----\n\nAn alternative approach to this can be to broaden the scope of the test by defining an alternative bootstrap mode of `DIRECT_DEPENDENCIES`.\n\n[source, java]\n----\npackage com.acme.myapp.moduleA;\n\n@RunWith(SpringRunner.class)\n@ModuleTest(mode = BootstrapMode.DIRECT_DEPENDENCIES)\npublic class ModuleATest { … }\n----\n\nThis will now inspect the module structure of the system, detect the dependency of Module A to Module B and include the latter into the component scan as well as auto-configuration and entity scan packages.\nIf the direct dependency has dependencies in turn, you now need to mock those using `@MockBean` in the test setup.\n\nIn case you want to run all modules up the dependency chain of the module to be tested use `BootstrapMode.ALL_DEPENDENCIES`.\nThis will cause all dependent modules to be bootstrapped but unrelated ones to be excluded.\n\n[[modules.general-recommendations]]\n===== General recommendations\n\nIf you find yourself having to mock too many components of upstream modules or include too many modules into the test run, it usually indicates that your modules are too tightly coupled.\nYou might want to look into replacing those direct invocations of beans in other modules by rather publishing an application event from the source module and consume it from the other module.\nSee \u003c\u003csos\u003e\u003e for further details.\n\n[[modules.complex]]\n==== More complex modules\n\nSometimes, a single package is not enough to capture all components of a single module and developers would like to organize code into additional packages.\nLet's assume Module B is using the following structure:\n\n[source]\n----\ncom.acme.myapp\n+ @Modulith ….MyApplication\n\ncom.acme.myapp.moduleA\n+ ….MyComponentA(MyComponentB)\n\ncom.acme.myapp.moduleB\n+ ….MyComponentB(MySupportingComponent, MyInternal)\no ….MySupportingComponent\ncom.acme.myapp.moduleB.internal\n+ ….MyInternal(MyOtherInternal, InternalSupporting)\no ….InternalSupporting\ncom.acme.myapp.moduleB.otherinternal\n+ ….MyOtherInternal\n----\n\nIn this case we have two supporting packages that contain components that depend on each other (`MyInternal` depending on `InternalSupport` in the same package as well as `MyOtherInternal` in the other supporting package).\nBy convention, on the module level, only dependencies to the top-level module package are allowed.\nI.e. any type residing in another module that depends on types in either `….moduleB.internal` or `moduleB.otherInternal` will cause an `@ModuleTest` to fail.\n\n[[modules.complex.named-interfaces]]\n===== Named interfaces\n\nIn case a single public package defining the module root is not enough, modules can define so called named interface packages that will constitute packages that are eligible targets for dependencies from components of other modules.\n\n[source]\n----\ncom.acme.myapp\n+ @Modulith ….MyApplication\n\ncom.acme.myapp.moduleA\n+ ….MyComponentA(MyComponentB)\n\ncom.acme.myapp.complex.api\n+ @NamedInterface(\"API\") ….package-info.java\ncom.acme.myapp.complex.spi\n+ @NamedInterface(\"SPI\") ….package-info.java\ncom.acme.myapp.complex.internal\no ….MyInternal\n----\n\nAs you can see, we have dedicated packages of the module annotated with `@NamedInterface`.\nThe annotation will cause each of the packages to be referable from other modules dependencies, whereas non-annotated packages of the module (`internal`) won't (including the module root package).\n\n[[architectural-rule-enforcement]]\n=== Enforcement of architectural rules\n\n[NOTE]\n.Conventions\n====\nicon:check-circle[] – already implemented\n\nicon:question-circle[] – not yet implemented\n====\n\nGiven the module conventions we can already implement a couple of derived rules:\n\nicon:check-circle[] _**Assume top-level module package the API package**_ -- If sub-packages are used, we could assume that only the top-level one contains API to be referred to from other modules.\n\nicon:check-circle[] _**Provide an annotation to be used on packages so that multiple different named interfaces to a module can be defined.**_\n\nicon:check-circle[] _**Prevent invalid dependencies into module internal package.**_ -- All module sub-packages by default except explicitly declared as named interface.\n\nicon:question-circle[] `allowedDependencies` would then have to use `moduleA.API`, `moduleB.SPI`. If a single named interface exists, referring to the module implicitly refers to the single only named interface.\n\nicon:question-circle[] _**Verify module setup**_ -- We can verify the validity of the module setup to prevent configuration errors to go unnoticed:\n\n* icon:question-circle[] Catch invalid module and named interface references in `allowedDependencies`.\n\nicon:question-circle[] _**Derive default allowed dependencies based on the Spring bean component tree**_ -- by default we can inspect the Spring beans in the individual modules, their dependencies and assume the beans structure describes the allowed dependency structure.\nThis can be overridden by explicitly declaring `@Module(allowedDependencies = …)` on the package level.\n\nicon:question-circle[] _**Correlate actual dependencies with the ones defined (implicit or explicit)**_ -- Even with dependencies only defined implicitly by the Spring bean structure, the code can contain ordinary type dependencies that violate the module structure.\n\nicon:question-circle[] _**No cycles on the module level**_ -- We should generally disallow cycles on the module level.\n\n== Sample applications\n\n* https://github.com/odrotbohm/spring-restbucks[Spring RESTBucks] - an implementation of the RESTBucks API from the ”REST in Practice” book. Primary a showcase for hypermedia APIs but still using Moduliths primarily for documentation purposes.\n* https://github.com/st-tu-dresden/salespoint[Salespoint] - a POS library developed by the TU Dresden to be used in the software engineering lab to teach third semester students how to build web applications with Spring Boot.\n\n== Ideas\n\n=== In the works\n\n* \u003c\u003cmodules, A default module programming model based on Java packages that can be customized using annotations\u003e\u003e\n* \u003c\u003cmodules.running-tests, A Spring Boot extension that allows bootstrapping individual modules in various modes\u003e\u003e\n* \u003c\u003carchitectural-rule-enforcement, Out of the box module dependency tests\u003e\u003e\n\n=== Unapproached yet\n\n* \u003c\u003capt-rule-verification, Rule verification via APT\u003e\u003e\n\n\n[[boot-module-tests]]\n=== Spring Boot based module tests\n\n==== Further ideas\n\n* As Spring https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#context-functionality-events[Application Events] are a recommended means to implement inter-module interaction, we could register an `ApplicationListener` that exposes API to easily verify events being triggered, event listeners being triggered etc.\n\n[[apt-rule-verification]]\n=== Rule verification via APT\n\nAssuming we're able to get an APT implemented that's run on top of the current code base, we could run the aforementioned verifications and issue compiler errors for violations.\n\n[[existing-tools]]\n== Existing tools\n\n* https://github.com/TNG/ArchUnit[ArchUnit] -- Tool to define allowed dependencies on a type and package based level, usually executed via JUnit.\n[[jqassistant]]\n* https://jqassistant.org/[jQAssistant] -- Broader tool to analyze projects using a Neo4j-based meta-model and concepts and constraints described via Cypher queries.\n* https://structurizr.com/[Structurizr] -- Software architecture description and visualization tool by Simon Brown.\nIncludes Spring integration via automatic stereotype annotation detection.\n\n[appendix]\n== Appendix\n\n[bibliography]\n=== Further resources\n\n- [[[safd]]] Simon Brown -- Software Architecture for Developers (https://leanpub.com/b/software-architecture[Books], https://softwarearchitecturefordevelopers.com/[Website])\n- [[[sos]]] Oliver Gierke -- Refactoring to a System of Systems (https://speakerdeck.com/olivergierke/refactoring-to-a-system-of-systems[Slidedeck], https://www.youtube.com/watch?v=VWefNT8Lb74[Recording])\n- [[[whoops]]] Oliver Gierke -- Whoops, where did my architecture go? (http://olivergierke.de/2013/01/whoops-where-did-my-architecture-go/[Webpage])\n\n[glossary]\n=== Glossary\nNamed Interface:: Given a module, a sub-set of types that constitute the API of the module, i.e. candidates for referral by other modules.\n\n=== Release instructions\n\n* `mvn versions:set -DnewVersion=$version -DgenerateBackupPoms=false`\n* Change `/scm/tag` im `pom.xml` to `$version`\n* Commit against release ticket id\n* Tag commit\n* Push commit and tag\n* `mvn clean deploy -Psonatype`\n* `mvn versions:set -DnewVersion=$snapshotVersion -DgenerateBackupPoms=false`\n* Commit against release ticket id with message \"Prepare next development iteration.\"\n* Push commit.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoduliths%2Fmoduliths","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoduliths%2Fmoduliths","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoduliths%2Fmoduliths/lists"}