欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實(shí)現(xiàn)六邊形架構(gòu)的三種不同方式詳解

 更新時(shí)間:2025年06月20日 08:13:53   作者:風(fēng)象南  
六邊形架構(gòu),也被稱為端口與適配器架構(gòu)或洋蔥架構(gòu),是一種將業(yè)務(wù)邏輯與外部依賴解耦的架構(gòu)模式,本文將介紹在SpringBoot中實(shí)現(xiàn)六邊形架構(gòu)的三種不同方式

一、六邊形架構(gòu)基本原理

1.1 核心概念

六邊形架構(gòu)由Alistair Cockburn于2005年提出,其核心思想是將應(yīng)用程序的內(nèi)部業(yè)務(wù)邏輯與外部交互隔離開來(lái)。這種架構(gòu)主要由三部分組成:

  • 領(lǐng)域(Domain) :包含業(yè)務(wù)邏輯和領(lǐng)域模型,是應(yīng)用程序的核心
  • 端口(Ports) :定義應(yīng)用程序與外部世界交互的接口
  • 適配器(Adapters) :實(shí)現(xiàn)端口接口,連接外部世界與應(yīng)用程序

1.2 端口分類

端口通常分為兩類:

  • 輸入端口(Primary/Driving Ports) :允許外部系統(tǒng)驅(qū)動(dòng)應(yīng)用程序,如REST API、命令行接口
  • 輸出端口(Secondary/Driven Ports) :允許應(yīng)用程序驅(qū)動(dòng)外部系統(tǒng),如數(shù)據(jù)庫(kù)、消息隊(duì)列、第三方服務(wù)

1.3 六邊形架構(gòu)的優(yōu)勢(shì)

  • 業(yè)務(wù)邏輯獨(dú)立性:核心業(yè)務(wù)邏輯不依賴于特定技術(shù)框架
  • 可測(cè)試性:業(yè)務(wù)邏輯可以在沒有外部依賴的情況下進(jìn)行測(cè)試
  • 靈活性:可以輕松替換技術(shù)實(shí)現(xiàn)而不影響業(yè)務(wù)邏輯
  • 關(guān)注點(diǎn)分離:明確區(qū)分了業(yè)務(wù)規(guī)則和技術(shù)細(xì)節(jié)
  • 可維護(hù)性:使代碼結(jié)構(gòu)更加清晰,便于維護(hù)和擴(kuò)展

二、經(jīng)典六邊形架構(gòu)實(shí)現(xiàn)

2.1 項(xiàng)目結(jié)構(gòu)

經(jīng)典六邊形架構(gòu)嚴(yán)格遵循原始設(shè)計(jì)理念,通過明確的包結(jié)構(gòu)分離領(lǐng)域邏輯和適配器:

src/main/java/com/example/demo/
├── domain/                  # 領(lǐng)域?qū)?br />│   ├── model/               # 領(lǐng)域模型
│   ├── service/             # 領(lǐng)域服務(wù)
│   └── port/                # 端口定義
│       ├── incoming/        # 輸入端口
│       └── outgoing/        # 輸出端口
├── adapter/                 # 適配器層
│   ├── incoming/            # 輸入適配器
│   │   ├── rest/            # REST API適配器
│   │   └── scheduler/       # 定時(shí)任務(wù)適配器
│   └── outgoing/            # 輸出適配器
│       ├── persistence/     # 持久化適配器
│       └── messaging/       # 消息適配器
└── application/             # 應(yīng)用配置
    └── config/              # Spring配置類

2.2 代碼實(shí)現(xiàn)

2.2.1 領(lǐng)域模型

// 領(lǐng)域模型
package com.example.demo.domain.model;

public class Product {
    private Long id;
    private String name;
    private double price;
    private int stock;
    
    // 構(gòu)造函數(shù)、getter和setter
    
    // 領(lǐng)域行為
    public boolean isAvailable() {
        return stock > 0;
    }
    
    public void decreaseStock(int quantity) {
        if (quantity > stock) {
            throw new IllegalArgumentException("Not enough stock");
        }
        this.stock -= quantity;
    }
}

2.2.2 輸入端口

// 輸入端口(用例接口)
package com.example.demo.domain.port.incoming;

import com.example.demo.domain.model.Product;
import java.util.List;
import java.util.Optional;

public interface ProductService {
    List<Product> getAllProducts();
    Optional<Product> getProductById(Long id);
    Product createProduct(Product product);
    void updateStock(Long productId, int quantity);
}

2.2.3 輸出端口

// 輸出端口(存儲(chǔ)庫(kù)接口)
package com.example.demo.domain.port.outgoing;

import com.example.demo.domain.model.Product;
import java.util.List;
import java.util.Optional;

public interface ProductRepository {
    List<Product> findAll();
    Optional<Product> findById(Long id);
    Product save(Product product);
    void deleteById(Long id);
}

2.2.4 領(lǐng)域服務(wù)實(shí)現(xiàn)

// 領(lǐng)域服務(wù)實(shí)現(xiàn)
package com.example.demo.domain.service;

import com.example.demo.domain.model.Product;
import com.example.demo.domain.port.incoming.ProductService;
import com.example.demo.domain.port.outgoing.ProductRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
public class ProductServiceImpl implements ProductService {
    
    private final ProductRepository productRepository;
    
    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    
    @Override
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }
    
    @Override
    public Optional<Product> getProductById(Long id) {
        return productRepository.findById(id);
    }
    
    @Override
    public Product createProduct(Product product) {
        return productRepository.save(product);
    }
    
    @Override
    @Transactional
    public void updateStock(Long productId, int quantity) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new RuntimeException("Product not found"));
        
        product.decreaseStock(quantity);
        productRepository.save(product);
    }
}

2.2.5 輸入適配器(REST API)

// REST適配器
package com.example.demo.adapter.incoming.rest;

import com.example.demo.domain.model.Product;
import com.example.demo.domain.port.incoming.ProductService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    private final ProductService productService;
    
    public ProductController(ProductService productService) {
        this.productService = productService;
    }
    
    @GetMapping
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable Long id) {
        return productService.getProductById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        return productService.createProduct(product);
    }
    
    @PutMapping("/{id}/stock")
    public ResponseEntity<Void> updateStock(
            @PathVariable Long id,
            @RequestParam int quantity) {
        productService.updateStock(id, quantity);
        return ResponseEntity.ok().build();
    }
}

2.2.6 輸出適配器(持久化)

// JPA實(shí)體
package com.example.demo.adapter.outgoing.persistence.entity;

import javax.persistence.*;

@Entity
@Table(name = "products")
public class ProductEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    private int stock;
    
    // 構(gòu)造函數(shù)、getter和setter
}

// JPA倉(cāng)庫(kù)
package com.example.demo.adapter.outgoing.persistence.repository;

import com.example.demo.adapter.outgoing.persistence.entity.ProductEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaProductRepository extends JpaRepository<ProductEntity, Long> {
}

// 持久化適配器
package com.example.demo.adapter.outgoing.persistence;

import com.example.demo.adapter.outgoing.persistence.entity.ProductEntity;
import com.example.demo.adapter.outgoing.persistence.repository.JpaProductRepository;
import com.example.demo.domain.model.Product;
import com.example.demo.domain.port.outgoing.ProductRepository;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
public class ProductRepositoryAdapter implements ProductRepository {
    
    private final JpaProductRepository jpaRepository;
    
    public ProductRepositoryAdapter(JpaProductRepository jpaRepository) {
        this.jpaRepository = jpaRepository;
    }
    
    @Override
    public List<Product> findAll() {
        return jpaRepository.findAll().stream()
            .map(this::mapToDomain)
            .collect(Collectors.toList());
    }
    
    @Override
    public Optional<Product> findById(Long id) {
        return jpaRepository.findById(id)
            .map(this::mapToDomain);
    }
    
    @Override
    public Product save(Product product) {
        ProductEntity entity = mapToEntity(product);
        ProductEntity savedEntity = jpaRepository.save(entity);
        return mapToDomain(savedEntity);
    }
    
    @Override
    public void deleteById(Long id) {
        jpaRepository.deleteById(id);
    }
    
    private Product mapToDomain(ProductEntity entity) {
        Product product = new Product();
        product.setId(entity.getId());
        product.setName(entity.getName());
        product.setPrice(entity.getPrice());
        product.setStock(entity.getStock());
        return product;
    }
    
    private ProductEntity mapToEntity(Product product) {
        ProductEntity entity = new ProductEntity();
        entity.setId(product.getId());
        entity.setName(product.getName());
        entity.setPrice(product.getPrice());
        entity.setStock(product.getStock());
        return entity;
    }
}

2.3 優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn):

  • 結(jié)構(gòu)清晰,嚴(yán)格遵循六邊形架構(gòu)原則
  • 領(lǐng)域模型完全獨(dú)立,不受技術(shù)框架影響
  • 適配器隔離了所有外部依賴
  • 高度可測(cè)試,可以輕松模擬任何外部組件

缺點(diǎn):

  • 代碼量較大,需要編寫更多的接口和適配器
  • 對(duì)象映射工作增加,需要在適配器中轉(zhuǎn)換領(lǐng)域?qū)ο蠛统志没瘜?duì)象
  • 可能感覺過度設(shè)計(jì),特別是對(duì)簡(jiǎn)單應(yīng)用程序
  • 學(xué)習(xí)曲線較陡峭,團(tuán)隊(duì)需要深入理解六邊形架構(gòu)

2.4 適用場(chǎng)景

  • 復(fù)雜的業(yè)務(wù)領(lǐng)域,需要清晰隔離業(yè)務(wù)規(guī)則
  • 長(zhǎng)期維護(hù)的核心系統(tǒng)
  • 團(tuán)隊(duì)已熟悉六邊形架構(gòu)原則
  • 需要靈活替換技術(shù)實(shí)現(xiàn)的場(chǎng)景

三、基于DDD的六邊形架構(gòu)

3.1 項(xiàng)目結(jié)構(gòu)

基于DDD的六邊形架構(gòu)結(jié)合了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的概念,進(jìn)一步豐富了領(lǐng)域?qū)樱?/p>

src/main/java/com/example/demo/
├── domain/                  # 領(lǐng)域?qū)?br />│   ├── model/               # 領(lǐng)域模型
│   │   ├── aggregate/       # 聚合
│   │   ├── entity/          # 實(shí)體
│   │   └── valueobject/     # 值對(duì)象
│   ├── service/             # 領(lǐng)域服務(wù)
│   └── repository/          # 倉(cāng)儲(chǔ)接口
├── application/             # 應(yīng)用層
│   ├── port/                # 應(yīng)用服務(wù)接口
│   │   ├── incoming/        # 輸入端口
│   │   └── outgoing/        # 輸出端口
│   └── service/             # 應(yīng)用服務(wù)實(shí)現(xiàn)
├── infrastructure/          # 基礎(chǔ)設(shè)施層
│   ├── adapter/             # 適配器
│   │   ├── incoming/        # 輸入適配器
│   │   └── outgoing/        # 輸出適配器
│   └── config/              # 配置類
└── interface/               # 接口層
    ├── rest/                # REST接口
    ├── graphql/             # GraphQL接口
    └── scheduler/           # 定時(shí)任務(wù)

3.2 代碼實(shí)現(xiàn)

3.2.1 領(lǐng)域模型

// 值對(duì)象
package com.example.demo.domain.model.valueobject;

public class Money {
    private final BigDecimal amount;
    private final String currency;
    
    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }
    
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Cannot add different currencies");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
    
    // 其他值對(duì)象方法
}

// 實(shí)體
package com.example.demo.domain.model.entity;

import com.example.demo.domain.model.valueobject.Money;

public class Product {
    private ProductId id;
    private String name;
    private Money price;
    private int stock;
    
    // 構(gòu)造函數(shù)、getter和setter
    
    // 領(lǐng)域行為
    public boolean isAvailable() {
        return stock > 0;
    }
    
    public void decreaseStock(int quantity) {
        if (quantity > stock) {
            throw new IllegalArgumentException("Not enough stock");
        }
        this.stock -= quantity;
    }
}

// 聚合根
package com.example.demo.domain.model.aggregate;

import com.example.demo.domain.model.entity.Product;
import com.example.demo.domain.model.valueobject.Money;

import java.util.ArrayList;
import java.util.List;

public class Order {
    private OrderId id;
    private CustomerId customerId;
    private List<OrderLine> orderLines = new ArrayList<>();
    private OrderStatus status;
    private Money totalAmount;
    
    // 構(gòu)造函數(shù)、getter和setter
    
    // 領(lǐng)域行為
    public void addProduct(Product product, int quantity) {
        if (!product.isAvailable() || product.getStock() < quantity) {
            throw new IllegalArgumentException("Product not available");
        }
        
        OrderLine orderLine = new OrderLine(product.getId(), product.getPrice(), quantity);
        orderLines.add(orderLine);
        recalculateTotal();
    }
    
    public void confirm() {
        if (orderLines.isEmpty()) {
            throw new IllegalStateException("Cannot confirm empty order");
        }
        this.status = OrderStatus.CONFIRMED;
    }
    
    private void recalculateTotal() {
        this.totalAmount = orderLines.stream()
            .map(OrderLine::getSubtotal)
            .reduce(Money.ZERO, Money::add);
    }
}

3.2.2 領(lǐng)域倉(cāng)儲(chǔ)接口

// 倉(cāng)儲(chǔ)接口
package com.example.demo.domain.repository;

import com.example.demo.domain.model.aggregate.Order;
import com.example.demo.domain.model.aggregate.OrderId;

import java.util.Optional;

public interface OrderRepository {
    Optional<Order> findById(OrderId id);
    Order save(Order order);
    void delete(OrderId id);
}

3.2.3 應(yīng)用服務(wù)接口

// 應(yīng)用服務(wù)接口
package com.example.demo.application.port.incoming;

import com.example.demo.application.dto.OrderRequest;
import com.example.demo.application.dto.OrderResponse;

import java.util.List;
import java.util.Optional;

public interface OrderApplicationService {
    OrderResponse createOrder(OrderRequest request);
    Optional<OrderResponse> getOrder(String orderId);
    List<OrderResponse> getCustomerOrders(String customerId);
    void confirmOrder(String orderId);
}

3.2.4 應(yīng)用服務(wù)實(shí)現(xiàn)

// 應(yīng)用服務(wù)實(shí)現(xiàn)
package com.example.demo.application.service;

import com.example.demo.application.dto.OrderRequest;
import com.example.demo.application.dto.OrderResponse;
import com.example.demo.application.port.incoming.OrderApplicationService;
import com.example.demo.application.port.outgoing.ProductRepository;
import com.example.demo.domain.model.aggregate.Order;
import com.example.demo.domain.model.aggregate.OrderId;
import com.example.demo.domain.model.entity.Product;
import com.example.demo.domain.repository.OrderRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class OrderApplicationServiceImpl implements OrderApplicationService {
    
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    
    public OrderApplicationServiceImpl(OrderRepository orderRepository, ProductRepository productRepository) {
        this.orderRepository = orderRepository;
        this.productRepository = productRepository;
    }
    
    @Override
    @Transactional
    public OrderResponse createOrder(OrderRequest request) {
        // 創(chuàng)建訂單領(lǐng)域?qū)ο?
        Order order = new Order(new CustomerId(request.getCustomerId()));
        
        // 添加產(chǎn)品
        for (OrderRequest.OrderItem item : request.getItems()) {
            Product product = productRepository.findById(new ProductId(item.getProductId()))
                .orElseThrow(() -> new RuntimeException("Product not found"));
            
            order.addProduct(product, item.getQuantity());
            
            // 減少庫(kù)存
            product.decreaseStock(item.getQuantity());
            productRepository.save(product);
        }
        
        // 保存訂單
        Order savedOrder = orderRepository.save(order);
        
        // 返回DTO
        return mapToDto(savedOrder);
    }
    
    @Override
    public Optional<OrderResponse> getOrder(String orderId) {
        return orderRepository.findById(new OrderId(orderId))
            .map(this::mapToDto);
    }
    
    @Override
    public List<OrderResponse> getCustomerOrders(String customerId) {
        return orderRepository.findByCustomerId(new CustomerId(customerId)).stream()
            .map(this::mapToDto)
            .collect(Collectors.toList());
    }
    
    @Override
    @Transactional
    public void confirmOrder(String orderId) {
        Order order = orderRepository.findById(new OrderId(orderId))
            .orElseThrow(() -> new RuntimeException("Order not found"));
        
        order.confirm();
        orderRepository.save(order);
    }
    
    private OrderResponse mapToDto(Order order) {
        // 映射邏輯
    }
}

3.2.5 輸入適配器(REST控制器)

// REST控制器
package com.example.demo.interface.rest;

import com.example.demo.application.dto.OrderRequest;
import com.example.demo.application.dto.OrderResponse;
import com.example.demo.application.port.incoming.OrderApplicationService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    private final OrderApplicationService orderService;
    
    public OrderController(OrderApplicationService orderService) {
        this.orderService = orderService;
    }
    
    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
        OrderResponse response = orderService.createOrder(request);
        return ResponseEntity.ok(response);
    }
    
    @GetMapping("/{orderId}")
    public ResponseEntity<OrderResponse> getOrder(@PathVariable String orderId) {
        return orderService.getOrder(orderId)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    @GetMapping("/customer/{customerId}")
    public List<OrderResponse> getCustomerOrders(@PathVariable String customerId) {
        return orderService.getCustomerOrders(customerId);
    }
    
    @PostMapping("/{orderId}/confirm")
    public ResponseEntity<Void> confirmOrder(@PathVariable String orderId) {
        orderService.confirmOrder(orderId);
        return ResponseEntity.ok().build();
    }
}

3.2.6 輸出適配器(JPA持久化)

// JPA實(shí)體
package com.example.demo.infrastructure.adapter.outgoing.persistence.entity;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "orders")
public class OrderJpaEntity {
    @Id
    private String id;
    
    private String customerId;
    
    @Enumerated(EnumType.STRING)
    private OrderStatusJpa status;
    
    private BigDecimal totalAmount;
    
    private String currency;
    
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "order_id")
    private List<OrderLineJpaEntity> orderLines = new ArrayList<>();
    
    // 構(gòu)造函數(shù)、getter和setter
}

// JPA倉(cāng)庫(kù)
package com.example.demo.infrastructure.adapter.outgoing.persistence.repository;

import com.example.demo.infrastructure.adapter.outgoing.persistence.entity.OrderJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface OrderJpaRepository extends JpaRepository<OrderJpaEntity, String> {
    List<OrderJpaEntity> findByCustomerId(String customerId);
}

// 適配器實(shí)現(xiàn)
package com.example.demo.infrastructure.adapter.outgoing.persistence;

import com.example.demo.domain.model.aggregate.Order;
import com.example.demo.domain.model.aggregate.OrderId;
import com.example.demo.domain.model.entity.CustomerId;
import com.example.demo.domain.repository.OrderRepository;
import com.example.demo.infrastructure.adapter.outgoing.persistence.entity.OrderJpaEntity;
import com.example.demo.infrastructure.adapter.outgoing.persistence.repository.OrderJpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
public class OrderRepositoryAdapter implements OrderRepository {
    
    private final OrderJpaRepository jpaRepository;
    
    public OrderRepositoryAdapter(OrderJpaRepository jpaRepository) {
        this.jpaRepository = jpaRepository;
    }
    
    @Override
    public Optional<Order> findById(OrderId id) {
        return jpaRepository.findById(id.getValue())
            .map(this::mapToDomain);
    }
    
    @Override
    public Order save(Order order) {
        OrderJpaEntity entity = mapToJpaEntity(order);
        OrderJpaEntity savedEntity = jpaRepository.save(entity);
        return mapToDomain(savedEntity);
    }
    
    @Override
    public void delete(OrderId id) {
        jpaRepository.deleteById(id.getValue());
    }
    
    @Override
    public List<Order> findByCustomerId(CustomerId customerId) {
        return jpaRepository.findByCustomerId(customerId.getValue()).stream()
            .map(this::mapToDomain)
            .collect(Collectors.toList());
    }
    
    private Order mapToDomain(OrderJpaEntity entity) {
        // 映射邏輯
    }
    
    private OrderJpaEntity mapToJpaEntity(Order order) {
        // 映射邏輯
    }
}

3.3 優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn):

  • 結(jié)合DDD概念,更豐富的領(lǐng)域模型
  • 更精確地表達(dá)業(yè)務(wù)規(guī)則和約束
  • 領(lǐng)域模型與持久化完全分離
  • 支持復(fù)雜業(yè)務(wù)場(chǎng)景和領(lǐng)域行為

缺點(diǎn):

  • 架構(gòu)復(fù)雜度進(jìn)一步增加
  • 學(xué)習(xí)曲線陡峭,需要同時(shí)掌握DDD和六邊形架構(gòu)
  • 對(duì)象映射工作更加繁重
  • 可能過度設(shè)計(jì),特別是對(duì)簡(jiǎn)單領(lǐng)域

3.4 適用場(chǎng)景

  • 復(fù)雜業(yè)務(wù)領(lǐng)域,有豐富的業(yè)務(wù)規(guī)則和約束
  • 大型企業(yè)應(yīng)用,特別是核心業(yè)務(wù)系統(tǒng)
  • 團(tuán)隊(duì)熟悉DDD和六邊形架構(gòu)
  • 長(zhǎng)期維護(hù)的系統(tǒng),需要適應(yīng)業(yè)務(wù)變化

四、簡(jiǎn)化版架構(gòu)實(shí)現(xiàn)

4.1 項(xiàng)目結(jié)構(gòu)

簡(jiǎn)化架構(gòu)采用更輕量級(jí)的方式實(shí)現(xiàn)六邊形架構(gòu)的核心理念,減少接口數(shù)量,簡(jiǎn)化層次結(jié)構(gòu):

src/main/java/com/example/demo/
├── service/                 # 服務(wù)層
│   ├── business/            # 業(yè)務(wù)服務(wù)
│   ├── model/               # 數(shù)據(jù)模型
│   └── exception/           # 業(yè)務(wù)異常
├── integration/             # 集成層
│   ├── database/            # 數(shù)據(jù)庫(kù)集成
│   ├── messaging/           # 消息集成
│   └── external/            # 外部服務(wù)集成
├── web/                     # Web層
│   ├── controller/          # 控制器
│   ├── dto/                 # 數(shù)據(jù)傳輸對(duì)象
│   └── advice/              # 全局異常處理
└── config/                  # 配置

4.2 代碼實(shí)現(xiàn)

4.2.1 數(shù)據(jù)模型

// 業(yè)務(wù)模型
package com.example.demo.service.model;

import lombok.Data;

@Data
public class Product {
    private Long id;
    private String name;
    private double price;
    private int stock;
    
    // 業(yè)務(wù)邏輯直接在模型中
    public boolean isAvailable() {
        return stock > 0;
    }
    
    public void decreaseStock(int quantity) {
        if (quantity > stock) {
            throw new IllegalArgumentException("Not enough stock");
        }
        this.stock -= quantity;
    }
}

4.2.2 集成層接口

// 數(shù)據(jù)庫(kù)集成接口
package com.example.demo.integration.database;

import com.example.demo.service.model.Product;
import java.util.List;
import java.util.Optional;

// 沒有復(fù)雜的端口和適配器分離,直接定義操作接口
public interface ProductRepository {
    List<Product> findAll();
    Optional<Product> findById(Long id);
    Product save(Product product);
    void deleteById(Long id);
}

4.2.3 業(yè)務(wù)服務(wù)

// 業(yè)務(wù)服務(wù)
package com.example.demo.service.business;

import com.example.demo.integration.database.ProductRepository;
import com.example.demo.service.model.Product;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
public class ProductService {
    
    private final ProductRepository productRepository;
    
    // 直接注入所需依賴
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }
    
    public Optional<Product> getProductById(Long id) {
        return productRepository.findById(id);
    }
    
    public Product createProduct(Product product) {
        return productRepository.save(product);
    }
    
    @Transactional
    public void updateStock(Long productId, int quantity) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new RuntimeException("Product not found"));
        
        product.decreaseStock(quantity);
        productRepository.save(product);
    }
}

4.2.4 控制器

// REST控制器
package com.example.demo.web.controller;

import com.example.demo.service.business.ProductService;
import com.example.demo.service.model.Product;
import com.example.demo.web.dto.ProductDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    private final ProductService productService;
    
    public ProductController(ProductService productService) {
        this.productService = productService;
    }
    
    @GetMapping
    public List<ProductDTO> getAllProducts() {
        return productService.getAllProducts().stream()
            .map(this::toDto)
            .collect(Collectors.toList());
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<ProductDTO> getProductById(@PathVariable Long id) {
        return productService.getProductById(id)
            .map(this::toDto)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }
    
    @PostMapping
    public ProductDTO createProduct(@RequestBody ProductDTO productDto) {
        Product product = toEntity(productDto);
        return toDto(productService.createProduct(product));
    }
    
    @PutMapping("/{id}/stock")
    public ResponseEntity<Void> updateStock(
            @PathVariable Long id,
            @RequestParam int quantity) {
        productService.updateStock(id, quantity);
        return ResponseEntity.ok().build();
    }
    
    private ProductDTO toDto(Product product) {
        ProductDTO dto = new ProductDTO();
        dto.setId(product.getId());
        dto.setName(product.getName());
        dto.setPrice(product.getPrice());
        dto.setStock(product.getStock());
        return dto;
    }
    
    private Product toEntity(ProductDTO dto) {
        Product product = new Product();
        product.setId(dto.getId());
        product.setName(dto.getName());
        product.setPrice(dto.getPrice());
        product.setStock(dto.getStock());
        return product;
    }
}

4.2.5 數(shù)據(jù)庫(kù)實(shí)現(xiàn)

// JPA實(shí)體 - 與業(yè)務(wù)模型類似
package com.example.demo.integration.database.entity;

import lombok.Data;
import javax.persistence.*;

@Entity
@Table(name = "products")
@Data
public class ProductEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    private int stock;
}

// JPA倉(cāng)庫(kù)
package com.example.demo.integration.database.repository;

import com.example.demo.integration.database.entity.ProductEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaProductRepository extends JpaRepository<ProductEntity, Long> {
}

// 倉(cāng)庫(kù)實(shí)現(xiàn)
package com.example.demo.integration.database;

import com.example.demo.integration.database.entity.ProductEntity;
import com.example.demo.integration.database.repository.JpaProductRepository;
import com.example.demo.service.model.Product;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
public class ProductRepositoryImpl implements ProductRepository {
    
    private final JpaProductRepository jpaRepository;
    
    public ProductRepositoryImpl(JpaProductRepository jpaRepository) {
        this.jpaRepository = jpaRepository;
    }
    
    @Override
    public List<Product> findAll() {
        return jpaRepository.findAll().stream()
            .map(this::toModel)
            .collect(Collectors.toList());
    }
    
    @Override
    public Optional<Product> findById(Long id) {
        return jpaRepository.findById(id)
            .map(this::toModel);
    }
    
    @Override
    public Product save(Product product) {
        ProductEntity entity = toEntity(product);
        return toModel(jpaRepository.save(entity));
    }
    
    @Override
    public void deleteById(Long id) {
        jpaRepository.deleteById(id);
    }
    
    private Product toModel(ProductEntity entity) {
        Product product = new Product();
        product.setId(entity.getId());
        product.setName(entity.getName());
        product.setPrice(entity.getPrice());
        product.setStock(entity.getStock());
        return product;
    }
    
    private ProductEntity toEntity(Product product) {
        ProductEntity entity = new ProductEntity();
        entity.setId(product.getId());
        entity.setName(product.getName());
        entity.setPrice(product.getPrice());
        entity.setStock(product.getStock());
        return entity;
    }
}

4.3 優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn):

  • 結(jié)構(gòu)簡(jiǎn)單,學(xué)習(xí)曲線平緩
  • 減少了接口和層次數(shù)量,代碼量更少
  • 遵循Spring框架慣例,對(duì)Spring開發(fā)者友好
  • 開發(fā)效率高,適合快速迭代
  • 仍然保持了業(yè)務(wù)邏輯和外部依賴的基本分離

缺點(diǎn):

  • 分離不如經(jīng)典六邊形架構(gòu)嚴(yán)格
  • 業(yè)務(wù)邏輯可能會(huì)混入非核心關(guān)注點(diǎn)
  • 領(lǐng)域模型不夠豐富
  • 對(duì)復(fù)雜業(yè)務(wù)場(chǎng)景支持有限

4.4 適用場(chǎng)景

  • 中小型應(yīng)用,業(yè)務(wù)邏輯相對(duì)簡(jiǎn)單
  • 需要快速開發(fā)和迭代的項(xiàng)目
  • 原型或MVP開發(fā)
  • 啟動(dòng)階段的項(xiàng)目,后期可能演進(jìn)到更嚴(yán)格的架構(gòu)

五、總結(jié)

六邊形架構(gòu)的核心價(jià)值在于將業(yè)務(wù)邏輯與技術(shù)細(xì)節(jié)分離,提高系統(tǒng)的可維護(hù)性、可測(cè)試性和靈活性。

無(wú)論選擇哪種實(shí)現(xiàn)方式,都應(yīng)該堅(jiān)持這一核心原則,保持領(lǐng)域模型的純粹性和邊界的清晰性。

需要特別說(shuō)明的是,架構(gòu)應(yīng)該服務(wù)于業(yè)務(wù),而非相反。選擇合適的架構(gòu)方式,應(yīng)以提高開發(fā)效率、系統(tǒng)質(zhì)量和業(yè)務(wù)適應(yīng)性為目標(biāo)。

以上就是SpringBoot實(shí)現(xiàn)六邊形架構(gòu)的三種不同方式詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot六邊形架構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論