Bar*_*son 9 java rest design-patterns microservices clean-architecture
https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
我对这种模式有一些疑问.数据库处于更高层,但实际上如何运作?例如,如果我有一个微服务,只是管理这个实体:
person{
id,
name,
age
}Run Code Online (Sandbox Code Playgroud)
其中一个用例是管理人员.管理人员正在保存/检索/ ..人员(=> CRUD操作),但要做到这一点,Usecase需要与数据库通信.但这将违反依赖性规则
使该体系结构工作的首要规则是依赖规则.此规则表明源代码依赖性只能指向内部.
如果我得到一个GET /person/{id}请求我的微服务应该像这样处理吗?
但使用依赖倒置将是违反
内圈中的任何东西都不能知道外圈中的某些东西.特别是,内圈中的代码不得提及在外圈中声明的内容的名称.这包括功能,类.变量或任何其他命名的软件实体.
跨越边界.在图的右下方是我们如何穿过圆边界的示例.它显示控制器和演示者与下一层中的用例通信.注意控制流程.它从控制器开始,在用例中移动,然后在演示者中执行.还要注意源代码依赖性.他们中的每一个都指向用例.
我们通常使用依赖性倒置原则来解决这个明显的矛盾.例如,在像Java这样的语言中,我们将安排接口和继承关系,使得源代码依赖性反对跨越边界的正确点处的控制流.
例如,考虑用例需要调用演示者.但是,此调用不能是直接的,因为这会违反依赖关系规则:内圈中不能提及外圈中的名称.因此我们在内部圆圈中使用用例调用接口(此处显示为用例输出端口),并使外部圆圈中的演示者实现它.
相同的技术用于跨越体系结构中的所有边界.我们利用动态多态来创建反对控制流的源代码依赖关系,这样无论控制流向何种方向,我们都可以符合依赖关系规则.
Use Case层是否应声明将由DB Package(框架和驱动程序层)实现的存储库接口
如果服务器收到一个GET /persons/1请求,PersonRest会创建一个PersonRepository,并将这个Repository + ID传递给ManagePerson :: getPerson函数,getPerson不知道PersonRepository但知道它实现的接口,所以它不违反任何规则吗?ManagePerson :: getPerson会使用该Repository来查找实体,并将Person Entity返回给PersonRest :: get,它会将Json Objekt返回给客户端吗?
遗憾的是英语不是我的母语,所以我希望你们能让我知道我是否理解了这种模式是正确的,也许可以回答我的一些问题.
Ty提前
数据库处于更高层,但实际上如何运作?
您可以在网关层中创建与技术无关的接口,并在db层中实现它.例如
public interface OrderRepository {
public List<Order> findByCustomer(Customer customer);
}
Run Code Online (Sandbox Code Playgroud)
实现在db层中
public class HibernateOrderRepository implements OrderRepository {
...
}
Run Code Online (Sandbox Code Playgroud)
在运行时,内层将注入外层实现.但是你没有源代码依赖.
您可以通过扫描import语句来查看.
其中一个用例是管理人员.管理人员正在保存/检索/ ..人员(=> CRUD操作),但要做到这一点,Usecase需要与数据库通信.但这将违反依赖性规则
不,这不会违反依赖规则,因为用例定义了他们需要的接口.db只是实现它.
如果使用maven管理应用程序依赖关系,您将看到db jar模块依赖于用例,而不是相反.但是将这些用例界面提取到自己的模块中会更好.
然后模块依赖关系看起来像这样
+-----+ +---------------+ +-----------+
| db | --> | use-cases-api | <-- | use cases |
+-----+ +---------------+ +-----------+
Run Code Online (Sandbox Code Playgroud)
这是依赖的反转,否则看起来像这样
+-----+ +-----------+
| db | <-- | use cases |
+-----+ +-----------+
Run Code Online (Sandbox Code Playgroud)
是的,这将是违规,因为Web层访问数据库层.更好的方法是Web层访问控制器层,该层访问用例层,依此类推.
要保持依赖性反转,您必须使用如上所示的接口来分离各层.
因此,如果要将数据传递到内层,则必须在内层引入一个接口,该接口定义获取所需数据的方法并在外层实现.
在控制器层中,您将指定一个这样的接口
public interface ControllerParams {
public Long getPersonId();
}
Run Code Online (Sandbox Code Playgroud)
在Web层中,您可以像这样实现您的服务
@Path("/person")
public PersonRestService {
// Maybe injected using @Autowired if you are using spring
private SomeController someController;
@Get
@Path("{id}")
public void getPerson(PathParam("id") String id){
try {
Long personId = Long.valueOf(id);
someController.someMethod(new ControllerParams(){
public Long getPersonId(){
return personId;
}
});
} catch (NumberFormatException e) {
// handle it
}
}
}
Run Code Online (Sandbox Code Playgroud)
乍一看似乎是样板代码.但请记住,您可以让其余框架将请求反序列化为java对象.而这个对象可能会实现ControllerParams.
如果您因此遵循依赖项反转规则和干净的体系结构,则永远不会在内层中看到外层类的import语句.
清洁体系结构的目的是主要业务类不依赖于任何技术或环境.由于依赖关系指向外层到内层,因此外层更改的唯一原因是内层更改.或者,如果您交换外层的实现技术.例如休息 - > SOAP
那么我们为什么要这么做呢?
Robert C. Martin在第5章面向对象编程中讲述了这一点.在段依赖倒置的最后,他说:
使用这种方法,在以OO语言编写的系统中工作的软件架构师可以完全控制系统中所有源代码依赖的方向.Thay并不局限于将这些依赖项与控制流对齐.无论调用哪个模块以及调用哪个模块,软件架构师都可以将源代码依赖关系指向任何一个分数.
那就是力量!
我想开发人员经常对控制流和源代码依赖性感到困惑.控制流通常保持不变,但源代码依赖性是相反的.这使我们有机会创建插件架构.每个接口都是插入点.因此可以进行交换,例如出于技术或测试原因.
编辑
gateway layer = interface OrderRepository =>不应该将OrderRepository-Interface放在UseCases中,因为我需要在该级别上使用crud操作?
我认为可以移入OrderRepository用例层.另一种选择是使用用例的输入和输出端口.用例的输入端口可能具有类似于存储库的方法,例如findOrderById,并将其适应于OrderRepository.对于持久性,它可以使用您在输出端口中定义的方法.
public interface UseCaseInputPort {
public Order findOrderById(Long id);
}
public interface UseCaseOutputPort {
public void save(Order order);
}
Run Code Online (Sandbox Code Playgroud)
仅使用a的区别OrderRepository在于用例端口仅包含特定于用例的存储库方法.因此,只有在用例发生变化时它们才会改变.所以他们只有一个责任,你尊重界面隔离原则.