我想知道你如何将java中的项目模块划分为monolith,以及稍后可以将模块转换为微服务的可能性。
我的个人命名如下:
com.company.shopapp.product
...product.domain (ddd, services, repositories, entities, aggregates, command handlers - everything with package scope)
...product.api (everything with public scope)
...product.controller (CQRS endpoints for commands in web perspective - (package scope))
...product.query(CQRS - package scope)
com.company.shopapp.sales
- domain
- api
- controller
- query
我们这里基本上是产品管理上下文和销售上下文作为包。
模块仅使用公共接口(api包)相互通信。在我的项目中,我使用" .. api.ProductFacade"集中沟通点。
当我的销售"模块增长我将通过实施" .. api.ProductFacade"将其转变为微服务。接口作为"休息"或"肥皂"客户端,另一方面,我将基于ProductFacade接口创建Endpoint / RestController。 Package" com.company.shopapp.product.api"将被转换为扩展库并添加到两个项目中。
编辑: 我可以使用@Feign库开箱即用。 https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#spring-cloud-feign-inheritance
整个想法感觉很好,但也许您有更好的方法来设计项目并确保将其分解为微服务不会破坏整个应用程序。
答案 0 :(得分:2)
TLDR:分别考虑组件和模块并建立他们的“接触点”
模块,如您的示例所示,看起来像横切结构,与推荐的微服务实践相当。因此,它们都可以是单个微服务的一部分。如果您打算使用DDD,那么您需要在包路径中包含有界的上下文名称。
在我自己的源代码中,我通常将({顶级)模块分开,如config
(加载和解析,好,配置),functional
用于功能核心,域模型,{{ 1}}用于管理并发,Akka actor结构,监视等,以及operational
,其中所有API,DB和MQ代码都存在。最后,模块adapters
,其中所有模块都已启动,接口绑定到实现。此外,对于较低级别的样板,算法等,您通常会有一些app
或utils
。
在一些建筑学校中,模块和组件之间存在明显的分离。虽然前者是源代码结构的一部分,但后者是运行时单元,它们消耗资源并以特定方式生存。
在您的情况下,微服务对应于此类组件。这些组件可以在同一个JVM中运行 - 而且你会得到一个整体。或者它们可以在(可能)单独的主机上的单独JVM中运行。然后你称他们为微服务。
所以,你需要:
commons
。我应该注意到,在较低级别,您可以在组件中使用通用源代码模块,因此它们可以在代码中具有一些交叉点。但是在更高级别的源代码将是独特的,因此您可以根据组件将其拆分为模块。
您可以使用Akka并在监督子树中运行每个组件,其中子树的主管是组件的主要角色。那么主要的actor定义将是你组件的主要模块。如果需要让组件进行通信,则应将相应的ActorRef作为配置参数传递给适配器。
你讲的是集中通信点,但在我看来,如果你坚持微服务范式和你的组件的高度自治,那么对于每个API调用,某人必须拥有一份合同。输入不同的DDD有界上下文交互模式。如果将它留在某个集中管理的模块中,每个组件都应该使用它,那么这就是API治理的一个案例。只要你是唯一的维护者,那可能很方便。但是当不同的开发人员(甚至是团队)拿走他们的部分时,你需要再次考虑新的条件做出这个决定。
稍后您将组件分开 - 然后您将URL传递给适配器而不是ActorRefs。
答案 1 :(得分:2)
微服务按功能和连接程度复合。 我用这种方法:
在拆分项目时,您可以分析按包查看的新公共依赖项,排除公共库,为每个微服务复制项目,删除不必要的代码,或者更改服务实现(例如,如果“可能的大服务”使用“可能的第二大服务” ),配置和构建。 在这种情况下,“大”意味着对某些东西进行全面的功能实现,可以横向扩展,或者出于其他原因需要微服务。
答案 2 :(得分:2)
我认为你的模块结构很好。但我建议你创建一个真正的“多模块”项目(link)。这样,使用其他模块的代码将生成编译错误。这将有助于您保持良好的愿望!
为了做到这一点,你必须将每个模块拆分为私有(实现)和 public (api,only interfaces)模块(通过执行此操作) ,你不需要'api'包。 实现模块可以依赖于任何公共模块,但不依赖于私有模块。
如果您在私有模块中将应用程序连接在一起,并且依赖注入,私有模块将没有“内部”依赖项! 私有模块不会有任何“编译时”依赖项,只有“运行时”依赖项。
我希望你觉得这很有用!
修改强> 您只需要一个额外的模块来引导应用程序!