你好,六角形建筑


在开发软件时,我们总是渴望体系结构,因为我们希望我们的软件在未来可以很容易地进行更改。

因此,架构出于可维护性的考虑而介入。在这里,可维护性意味着我们可以在不干扰旧代码的情况下轻松地合并更改。

软件中的每一个新增功能都伴随着风险:破坏程序的风险、可维护性、刚性、依赖性等等,有很多种不同程度的风险,我们称之为技术债务。

技术债务与可维护性成反比关系,使用我们想要降低风险的架构风格,或者我应该说在添加新功能时保持一致。

我们体验到,当我们开始编写代码时,添加功能是很容易的,而逐渐地,除非我们为该项目使用适当的架构,否则添加功能会变得越来越困难。

六边形建筑

在一句话中,六角形建筑说:核心业务逻辑通过契约与应用程序的其他部分通信。

您的软件开发逻辑应该通过契约或Java接口与其他部分通信,即数据库、任何JMS队列、任何平面文件、HTTP请求、FTP等。

这种设计的好处在于,无论输入或输出是什么,每个通道都必须实现相同的契约/接口来与我们的软件对话。因此,从我们的角度来看,所有通道都是相同的,因为所有通道都实现相同的合同,因此我们的软件可以处理任何类型的输入和输出。

优势

  1. 轻松集成任何通道,如平面文件、RDBMS、NoSQL、http、FTP、JMS等。

  2. 我们的软件可以很容易地进行测试,因为在需要实现合同时很容易创建模拟。

  3. 添加新需求意味着添加插件或执行合同。

  4. 适当分离关注点。

  5. 通过合同保持IOC的高水平和低水平谈判。

有时我们称六边形架构为端口和适配器或洋葱架构。对于这些样式,我们为每个通道都有一个端口,例如一个数据库。

我们要执行的任务包括:

  1. 履行合同,与我们的软件对话。正如数据库API所说的,JDBC本身是一个契约,或者Hibernate本身是一个框架,我们需要创建一个GOF适配器模式,在该模式中我们可以实现一种策略,将JDBC相关的操作转换为我们的契约相关的操作。

  2. 将此适配器插入我们的端口以与此通道通信。

设计提纲

软件合同

package com.example.architecture.hexagonal;
public interface IPersists<T,TCOMMAND> {
   public void save(T t,TCOMMAND commandObject);
   public void delete(T t,TCOMMAND commandObject);
}

这是与我们软件对话的总合同。任何输出通道都应该实现该约定。

在这里,我举了一个示例,说明它如何与将数据保存在数据库中的JPA实体进行对话。

DataBaseChannelAdapter.java

package com.example.architecture.hexagonal;
public class DataBaseChannelAdapter implements IPersists<EmployeeDomainObject,EmployeeCommand>{
   public void save(EmployeeDomainObject t, EmployeeCommand commandObject) { 
       String underLyingJPAEntity = commandObject.getEntityClass();
       System.out.println("call save on " + underLyingJPAEntity);   
   }
   public void delete(EmployeeDomainObject t, EmployeeCommand commandObject) {
       String underLyingJPAEntity = commandObject.getEntityClass();
       System.out.println("call delete on " + underLyingJPAEntity);   
   }
}

因为JPA实体使用一些与JPA相关的注释,所以我没有将此JPA实体包括在我们的域中。我使用JPA框架作为我们软件域的外部通道,DataBaseChannelAdapter采用我们的核心域Employee对象和Command对象,该对象告诉适配器调用哪个JPA实体。

EmployeeDomainObject

package com.example.architecture.hexagonal;
public class EmployeeDomainObject {
   public String name;
   public String address;
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getAddress() {
      return address;
   }
   public void setAddress(String address) {
      this.address = address;
   }
   @Override
  public String toString() {
      return "EmployeeDomainObject [name=" + name + ", address=" + address
              + "]";
   }
}

我们的核心域Employee对象不依赖于任何框架,因此我可以通过适配器将任何框架插入其中。例如,我可以轻松地将DatabaseAdapter更改为FileAdepter,以便将我们的核心域对象保存到File中。

EmployeeCommand.java

package com.example.architecture.hexagonal;
public class EmployeeCommand{
   public String entityClass;
   public String getEntityClass() {
      return entityClass;
   }
   public void setEntityClass(String entityClass) {
      this.entityClass = entityClass;
   }
   @Override
   public String toString() {
      return "EmployeeCommand [entityClass=" + entityClass + "]";
   }
}

此命令对象帮助适配器将核心域对象转换为基础输出通道(数据库或文件)。

EmployeeDomainDao.java

package com.example.architecture.hexagonal;
public class EmployeeDomainDao<T,TCommand>{ 
   IPersists<T,TCommand> adapter;
   public void save(T t,TCommand commandObject)   {
      adapter.save(t, commandObject);
   }
   public void delete(T t,TCommand commandObject)   {
      adapter.delete(t, commandObject);
   }
   public IPersists<T, TCommand> getAdapter() {
      return adapter;
   }
   public void setAdapter(IPersists<T, TCommand> adapter) {
      this.adapter = adapter;
   }
}

这是我们的核心域持久层。基于适配器,它将调用确切的输出通道并持久化我们的域对象。

是时候测试我们的软件设计了

Main.java

package com.example.architecture.hexagonal;
public class Main {
   public static void main(String[] args) {
     EmployeeDomainDao<EmployeeDomainObject,EmployeeCommand> dao = new EmployeeDomainDao<EmployeeDomainObject,EmployeeCommand>();
      IPersists<EmployeeDomainObject,EmployeeCommand> adapter= new DataBaseChannelAdapter();
      dao.setAdapter(adapter);
      EmployeeDomainObject emp = new EmployeeDomainObject();
      emp.setName("Shamik Mitra");
      emp.setAddress("India,Kolkata");    
      EmployeeCommand command = new EmployeeCommand();
     command.setEntityClass("com.employeemanagement.entity.EmployeeJpaEntity");
      dao.save(emp, command);
      dao.delete(emp, command);
   }
}

输出量

六角形层

Object StateEmployeeDomainObject [name=Shamik Mitra, address=India,Kolkata]
call save on com.employeemanagement.entity.EmployeeJpaEntity
Object StateEmployeeDomainObject [name=Shamik Mitra, address=India,Kolkata]
call delete on com.employeemanagement.entity.EmployeeJpaEntity


图片由谷歌提供

应用程序域:正如我前面所说的,它是所有与软件相关的业务逻辑和验证进行的软件。这是每个外部模块通过合同进行对话的模块。

应用/中介层:有点像服务层,它采用框架层或外层,并根据域层契约进行必要的修改,与域层进行对话,或者以同样的方式将结果从域返回给框架层。它位于域和框架层之间。

框架层:此层用于输入/输出通道,也称为。并使用适配器来适配数据,并根据我们的软件合同对其进行转换。