说明

六边形架构又称“端口和适配器模式”,是Alistair Cockburn提出的一种具有对称性特征的架构风格。在这种架构中,系统通过适配器的方式与外部交互,将应用服务于领域服务封装在系统内部。

架构图

每一种外部系统都有一个适配器与之对应,外界通过应用层API与内部交互

六边形架构还是一种分层架构,如上图所示,它被分为了三层:端口适配器、应用层与领域层。而端口又可以分为输入端口和输出端口。

输入端口

用于系统提供服务时暴露API接口,接受外部客户系统的输入,并客户系统的输入转化为程序内部所能理解的输入。系统作为服务提供者是对外的接入层可以看成是输入端口。

输出端口

为系统获取外部服务提供支持,如获取持久化状态、对结果进行持久化,或者发布领域状态的变更通知(如领域事件)。系统作为服务的消费者获取服务是对外的接口(数据库、缓存、消息队列、RPC调用)等都可以看成是输入端口。

应用层

定义系统可以完成的工作,很薄的一层。它并不处理业务逻辑通过协调领域对象或领域服务完成业务逻辑,并通过输入端口输出结果。也可以在这一层进行事物管理。

领域层

负责表示业务概念、规则与状态,属于业务的核心。

应用层与领域层的不变性可以保证核心领域不受外部的干扰,而端口的可替换性可以很方便的对接不用的外部系统。

序列图

业务处理过程

源码演示

通过一个简单客户信息管理(增删改查)来演示以上叙述中的一些概念。这里使用spring-web实现REST API,通过内存HashMap实现领域对象存储与检索。

新建Customer领域模型
public class Customer {
private String id;
private String firstName;
private String lastName;
protected Customer() {
}
public Customer(String id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public String getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void changeFirstName(String firstName) {
this.firstName = firstName;
}
}
定义仓储接口
public interface CustomerRepository {
Customer get(String id);
void add(Customer customer);
void update(Customer customer);
Collection all();
}
实现应用层服务
@Component
public class CustomerApplication {
private CustomerRepository repository;
@Autowired
public CustomerApplication(CustomerRepository repository) {
this.repository = repository;
}
public void create(CreateCustomerCommand command) {
Customer customer = new Customer(UUID.randomUUID().toString(),
command.getFirstName());
repository.add(customer);
}
public Object fetch(String id) {
return repository.get(id);
}
public void changeFirstName(String id, String firstName) {
Customer customer = repository.get(id);
assert customer != null;
customer.changeFirstName(firstName);
repository.update(customer);
}
public Collection> all() {
return repository.all();
}
}
实现输入接口
@RestController
public class CustomerController {
private CustomerApplication application;
@Autowired
public CustomerController(CustomerApplication application) {
this.application = application;
}
@PostMapping("/customer")
public ResponseEntity create(@RequestParam String firstName) {
application.create(new CreateCustomerCommand(firstName));
return ResponseEntity.ok(null);
}
@GetMapping("/customer/{id}")
public ResponseEntity get(@PathVariable("id") String id) {
return ResponseEntity.ok(application.fetch(id));
}
@PatchMapping("/customer/{id}")
public ResponseEntity changeFirstName(@PathVariable("id") String id,
@RequestParam String firstName) {
application.changeFirstName(id, firstName);
return ResponseEntity.ok(null);
}
@GetMapping("/customers")
public ResponseEntity all() {
return ResponseEntity.ok(application.all());
}
}
实现仓储接口
@Component
public class InMemoryCustomerRepository implements CustomerRepository {
Map customerMap = new ConcurrentHashMap<>();
@Override
public Customer get(String id) {
return customerMap.get(id);
}
@Override
public void add(Customer customer) {
customerMap.put(customer.getId(), customer);
}
@Override
public void update(Customer customer) {
customerMap.put(customer.getId(), customer);
}
@Override
public Collection all() {
return Collections.unmodifiableCollection(customerMap.values());
}
}