同时为整体和微服务设计java项目

时间:2017-11-28 09:25:20

标签: java scope package domain-driven-design microservices

我想知道你如何将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

整个想法感觉很好,但也许您有更好的方法来设计项目并确保将其分解为微服务不会破坏整个应用程序。

3 个答案:

答案 0 :(得分:2)

TLDR:分别考虑组件和模块并建立他们的“接触点”

模块,如您的示例所示,看起来像横切结构,与推荐的微服务实践相当。因此,它们都可以是单个微服务的一部分。如果您打算使用DDD,那么您需要在包路径中包含有界的上下文名称。

在我自己的源代码中,我通常将({顶级)模块分开,如config(加载和解析,好,配置),functional用于功能核心,域模型,{{ 1}}用于管理并发,Akka actor结构,监视等,以及operational,其中所有API,DB和MQ代码都存在。最后,模块adapters,其中所有模块都已启动,接口绑定到实现。此外,对于较低级别的样板,算法等,您通常会有一些apputils

在一些建筑学校中,模块和组件之间存在明显的分离。虽然前者是源代码结构的一部分,但后者是运行时单元,它们消耗资源并以特定方式生存。

在您的情况下,微服务对应于此类组件。这些组件可以在同一个JVM中运行 - 而且你会得到一个整体。或者它们可以在(可能)单独的主机上的单独JVM中运行。然后你称他们为微服务。

所以,你需要:

  • 使每个组件的源代码自治,以便可以在单独的运行时空间(如类加载器,线程,线程池,actor系统子树)中启动它。因此,你需要一些发射器来实现它。然后,您就可以通过commons
  • 拨打电话
  • 在您的代码中引入一些模块,每个模块都包含单个组件的语义。这样您就可以从代码中了解组件的范围。
  • 组件之间的抽象通信,以便您可以使用适配器(源代码模块)通过网络进行通信,或使用内部JVM机制,如过程调用或Akka的消息传递。

我应该注意到,在较低级别,您可以在组件中使用通用源代码模块,因此它们可以在代码中具有一些交叉点。但是在更高级别的源代码将是独特的,因此您可以根据组件将其拆分为模块。

您可以使用Akka并在监督子树中运行每个组件,其中子树的主管是组件的主要角色。那么主要的actor定义将是你组件的主要模块。如果需要让组件进行通信,则应将相应的ActorRef作为配置参数传递给适配器。

你讲的是集中通信点,但在我看来,如果你坚持微服务范式和你的组件的高度自治,那么对于每个API调用,某人必须拥有一份合同。输入不同的DDD有界上下文交互模式。如果将它留在某个集中管理的模块中,每个组件都应该使用它,那么这就是API治理的一个案例。只要你是唯一的维护者,那可能很方便。但是当不同的开发人员(甚至是团队)拿走他们的部分时,你需要再次考虑新的条件做出这个决定。

稍后您将组件分开 - 然后您将URL传递给适配器而不是ActorRefs。

答案 1 :(得分:2)

微服务按功能和连接程度复合。 我用这种方法:

  • com.company.product:
    • 可能的大服务:
      • 配置
      • 服务
    • 可能的第二大服务:
      • 配置
      • 服务
    • 配置
    • 服务//可能永远不会分开
    • domain // common domain

在拆分项目时,您可以分析按包查看的新公共依赖项,排除公共库,为每个微服务复制项目,删除不必要的代码,或者更改服务实现(例如,如果“可能的大服务”使用“可能的第二大服务” ),配置和构建。 在这种情况下,“大”意味着对某些东西进行全面的功能实现,可以横向扩展,或者出于其他原因需要微服务。

答案 2 :(得分:2)

我认为你的模块结构很好。但我建议你创建一个真正的“多模块”项目(link)。这样,使用其他模块的代码将生成编译错误。这将有助于您保持良好的愿望!

为了做到这一点,你必须将每个模块拆分为私有(实现)和 public (api,only interfaces)模块(通过执行此操作) ,你不需要'api'包。 实现模块可以依赖于任何公共模块,但不依赖于私有模块。

如果您在私有模块中将应用程序连接在一起,并且依赖注入,私有模块将没有“内部”依赖项! 私有模块不会有任何“编译时”依赖项,只有“运行时”依赖项。

此处快速模块依赖关系图:enter image description here

我希望你觉得这很有用!

修改 您只需要一个额外的模块来引导应用程序!