DDD书店系统设计案例,小白也能掌握的技巧,值得收藏

领域驱动设计(Domain-Driven Design,简称DDD)是一种软件设计方法,它强调以业务领域为中心进行软件开发,通过结合业务专家的知识和软件开发者的专业技术,创建出能够准确反映业务需求的软件系统。

业务范围描述:

DDD通常应用于复杂的业务领域,这些领域具有以下特点:

  • 复杂的业务逻辑:业务规则复杂,难以用简单的方法描述或实现。
  • 专业性强:业务领域需要专业知识和深入理解。
  • 持续变化:业务需求经常变化,需要软件系统能够灵活适应。
  • 高度的业务价值:软件系统对业务流程和决策具有重要影响。

DDD特性:

  1. 领域模型:创建一个丰富的领域模型来表达业务概念和业务逻辑。
  2. 统一语言(Ubiquitous Language) :开发团队和业务专家使用统一的语言来沟通,减少误解。
  3. 限界上下文(Bounded Context) :明确定义系统的边界,每个限界上下文内部有一致的模型和语言。
  4. 实体(Entity)和值对象(Value Object) :区分具有唯一标识的实体和描述性的值对象。
  5. 聚合(Aggregate) :一组相关对象的集合,它们一起作为数据修改的单元。
  6. 领域服务(Domain Service) :不属于任何实体或值对象的领域逻辑。
  7. 领域事件(Domain Event) :表示领域中发生的有意义的业务事件。
  8. 仓储(Repository) :领域模型和数据存储之间的抽象层。
  9. 应用层(Application Layer) :协调领域层对象以完成用户请求。
  10. 持续重构:随着对业务领域的深入理解,不断重构领域模型。

DDD优点:

  1. 提高业务理解:通过统一语言和领域模型,开发团队可以更深入地理解业务。
  2. 增强沟通效率:统一语言减少了开发团队和业务专家之间的沟通障碍。
  3. 更好的软件设计:领域模型帮助设计出更符合业务需求的软件系统。
  4. 提高系统的可维护性:清晰的模型和限界上下文使得系统更易于维护和扩展。
  5. 适应快速变化:DDD的设计原则使得系统能够快速适应业务需求的变化。
  6. 促进领域专家的参与:领域专家在设计过程中扮演重要角色,提高了设计的准确性。
  7. 提高代码质量:通过关注领域逻辑和业务规则,提高了代码的质量和可读性。
  8. 支持复杂业务场景:DDD提供了一套方法论来处理复杂的业务场景。

书店系统业务需求:

  • 用户可以浏览书籍。
  • 用户可以搜索书籍。
  • 用户可以购买书籍。
  • 书籍有库存限制。
  • 用户可以查看订单状态。
  • 订单可以有不同的支付状态。

书店领域模型设计范围:

  1. 实体(Entities)

    • Book:具有唯一ISBN,包含标题、作者、价格和类型等属性。
    • User:用户实体,包含用户信息和订单列表。
    • Order:订单实体,包含订单详情和支付状态。
  2. 值对象(Value Objects)

    • Address:地址信息,不可变。
    • PaymentDetails:支付详情,包括支付方式和支付信息。
  3. 聚合(Aggregates)

    • Book Aggregate:包含Book和相关的值对象。
    • Order Aggregate:包含Order和关联的值对象,如Address和PaymentDetails。
  4. 领域服务(Domain Services)

    • InventoryService:管理库存。
    • OrderService:处理订单逻辑,如创建订单、支付订单。
  5. 领域事件(Domain Events)

    • BookAddedEvent:新书添加到库存时触发。
    • OrderPlacedEvent:新订单创建时触发。
  6. 仓储(Repositories)

    • BookRepository:提供对书籍数据的访问。
    • OrderRepository:提供对订单数据的访问。
  7. 应用层(Application Layer)

    • 协调领域层对象以完成用户请求。
  8. 控制器(Controllers)

    • BookController:处理书籍浏览和搜索请求。
    • OrderController:处理订单创建和支付请求。

UML设计:

UML设计说明:

  • 这个UML类图展示了在线书店系统中的主要领域模型组件和它们之间的关系。
  • Book 类表示书籍实体,它与 BookRepository 通过聚合关系相连。
  • User 类表示用户实体,持有订单列表,并且可以下单。
  • Order 类表示订单实体,包含订单详情和支付状态,与 OrderRepository 相连。
  • AddressPaymentDetails 是值对象,用于描述订单的地址和支付信息。
  • InventoryServiceOrderService 是领域服务,处理特定的业务逻辑。
  • 控制器(BookControllerOrderController)没有在UML图中显示,但它们是应用层的一部分,负责处理用户的HTTP请求。

DDD案例代码

实体和值对象定义:

Book.java

java 复制代码
public class Book {
    private String isbn;
    private String title;
    private String author;
    private BigDecimal price;
    private String genre;

    // Constructors, getters and setters
}

Address.java

java 复制代码
public class Address {
    private final String street;
    private final String city;
    private final String zipCode;

    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }

    // Getters
}

PaymentDetails.java

java 复制代码
public class PaymentDetails {
    private final String paymentMethod;
    private final String cardNumber;

    public PaymentDetails(String paymentMethod, String cardNumber) {
        this.paymentMethod = paymentMethod;
        this.cardNumber = cardNumber;
    }

    // Getters
}

聚合根和领域服务:

Order.java

java 复制代码
public class Order {
    private String orderId;
    private User user;
    private List<Book> books = new ArrayList<>();
    private BigDecimal total;
    private String status;
    private Address address;
    private PaymentDetails paymentDetails;

    // Constructors, getters and setters

    public void addBook(Book book) {
        books.add(book);
        recalculateTotal();
    }

    private void recalculateTotal() {
        total = books.stream()
            .map(Book::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

InventoryService.java

java 复制代码
@Service
public class InventoryService {
    public boolean checkAvailability(Book book, int quantity) {
        // Logic to check if the book is available in the required quantity
        return true; // Simplified for example
    }
}

OrderService.java

java 复制代码
@Service
public class OrderService {
    private final InventoryService inventoryService;
    private final OrderRepository orderRepository;

    public OrderService(InventoryService inventoryService, OrderRepository orderRepository) {
        this.inventoryService = inventoryService;
        this.orderRepository = orderRepository;
    }

    public Order createOrder(User user, List<Book> books, Address address, PaymentDetails paymentDetails) {
        if (!inventoryService.checkAvailability(books)) {
            throw new InventoryException("Books not available");
        }
        Order order = new Order(/* parameters */);
        order.setBooks(books);
        order.setAddress(address);
        order.setPaymentDetails(paymentDetails);
        // Process payment and set order status
        return orderRepository.save(order);
    }
}

仓储接口:

BookRepository.java

java 复制代码
public interface BookRepository extends JpaRepository<Book, Long> {
    List<Book> findAll();
    Optional<Book> findById(String isbn);
}

OrderRepository.java

java 复制代码
public interface OrderRepository extends JpaRepository<Order, String> {
    List<Order> findAll();
    void save(Order order);
}

控制器:

BookController.java

java 复制代码
@RestController
@RequestMapping("/api/books")
public class BookController {
    private final BookRepository bookRepository;

    @Autowired
    public BookController(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @GetMapping
    public ResponseEntity<List<Book>> getAllBooks() {
        return ResponseEntity.ok(bookRepository.findAll());
    }
}

OrderController.java

java 复制代码
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private final OrderService orderService;

    @Autowired
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public ResponseEntity<Order> createOrder(@RequestBody OrderRequest orderRequest) {
        try {
            Order order = orderService.createOrder(
                orderRequest.getUser(),
                orderRequest.getBooks(),
                orderRequest.getAddress(),
                orderRequest.getPaymentDetails()
            );
            return ResponseEntity.ok(order);
        } catch (InventoryException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        }
    }
}
相关推荐
字节跳动数据库14 分钟前
文章分享——相似函数处理方法
人工智能·后端·程序员
云技纵横14 分钟前
@Transactional 失效的 7 种场景:第 5 种最难排查
后端
用户67570498850232 分钟前
你知道 Go 结构体和结构体指针调用的区别吗?一文带你彻底搞懂!
后端·go
程序员cxuan1 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
用户6757049885021 小时前
面试官问“装饰器模式”,这样回答薪资多要 3000!
后端
tntxia1 小时前
Geo Scene域名修改引起的一些问题
后端
用户298698530141 小时前
Java 实现 Word 文档加密与权限解除
java·后端
vanuan1 小时前
给你的A2A-Agent加把锁-认证鉴权实战指南
后端
Yeats_Liao2 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构