DDD领域驱动设计基本理解

一篇文章认识领域驱动设计(DDD),实现领域建模解决复杂业务问题 领域驱动设计(DDD)是一种软件开发方法论,着重于解决复杂领域问题的建模和实现。 一篇文章认识领域驱动设计的基本概念和原则、如何实现领域建模,解决复杂业务问题、 微服务与DDD的关系,DDD落地到微服务的设计原则。 阅读详情

DDD是一种软件设计思想和方法论,以领域为核心构建软件设计体系,将业务模型抽象成领域模型进行拆解和封装。本文简要介绍DDD的基本概念和常用的分层设计架构,并结合业务场景进行领域驱动设计的实战分析,以加深理解。


1、DDD领域驱动设计基本概念理解
1.1 什么是DDD领域驱动设计?

领域驱动设计DDD(Domain Driven Design)是一种从系统分析到软件建模的设计思想和方法论,最早在2004年由Eric Evans在著作《Domain-Driven Design –Tackling Complexity in the Heart of Software》中提出相应的概念。其核心思想是以领域为核心驱动力构建软件设计体系,并围绕业务概念抽象出领域模型,通过领域和边界划分将复杂的业务模型抽象化、简单化,最终实现复杂软件应用系统的拆解和封装。DDD不仅可以用于微服务设计,还可以很好地应用于企业中台的设计,也适用于传统的单体应用。

在DDD中分为战略设计和战术设计:

  • 战略设计:主要从业务视角出发,建立业务领域模型、划分领域边界、建立通用语言的限界上下文。在战略设计中,需要对领域进行全面的了解和分析,探究业务的规则和本质,以及领域未来的趋势发展和可能变化。
  • 战术设计:从技术视角出发,侧重于领域模型的技术实现完成软件开发和落地,包括:实体、值对象、聚合、工厂、仓库、领域服务、领域事件等代码逻辑的设计和实现。战术设计关注的是领域中的具体情境和场景,需要针对具体的问题进行具体的分析和设计,以满足业务需求。
1.2 DDD领域驱动设计和微服务关系

这些年随着软硬件技术的发展,软件设计架构也从最早的单机架构(BS/CS)、到集中式架构,再到如今微服务(MicroServices)分布式架构。

  • 单机架构:采用面向过程的设计方法,系统包括应用客户端层和数据库层,整个系统围绕数据库驱动设计和开发。
  • 集中式架构:采用面向对象的设计方法,经典的三层架构包括业务接入层、业务逻辑层和数据访问层。这种集中式架构容易在某一层变得臃肿,另外受限于集中式的限制,可扩展性差,架构上不灵活。
  • 分布式微服务架构:微服务架构将应用程序拆分为一系列相互协作的微服务,每个微服务负责一个特定的业务领域或业务功能,从而实现应用之间的解耦,同时解决单体应用扩展性和灵活性问题。在微服务架构中,每个微服务都是独立的,可以使用不同的编程语言、数据存储和框架,这使得每个微服务可以独立地演进和部署。

在这里插入图片描述

DDD的概念最早在2004年就提出来,之后一直不温不火直到微服务的概念出现,也可以说DDD的流行和微服务的发展有一定的关系。微服务的兴起使得越来越多的开发人员开始关注如何将应用程序划分成一系列相互协作的微服务,并且每个微服务需要实现自己的业务逻辑。在这个过程中,开发人员发现他们需要一种更好的方式来划分业务领域边界,建立正确的业务模型和通用语言,以便更好地实现微服务的独立性和可维护性。

DDD正是一种能够帮助开发人员更好地设计和实现业务领域的模式。通过将业务领域作为核心,DDD可以帮助开发人员更好地理解业务需求,建立正确的业务模型和通用语言,从而更好地划分微服务边界,从而更好地实现微服务的独立性和可维护性。

1.3 DDD领域驱动设计中基本概念

领域驱动设计DDD中的核心概念如下图所示,包括:

  • 战略设计:包括领域/子域、统一语言、限界上下文等概念
  • 战术设计:使用这些模式来捕获和传递领域中的概念、关系、规则
    • 代表领域的概念:如实体、值对象、领域服务、领域事件等
    • 用于管理对象的生命周期:如聚合、工厂、资源库等
    • 用于集成或跟踪:领域事件、事件溯源等

在这里插入图片描述

1.3.1 统一语言

DDD中的统一语言(Ubiquitous Language)是一种用于描述业务领域中的概念、规则和流程的语言。它是一种由领域专家、开发人员和用户共同理解的通用语言,而不是一种特定的编程语言或框架。

统一语言贯穿于整个开发过程,从需求分析到设计再到编码。它应该用于描述业务领域中的实体、值对象、聚合、领域服务等概念,同时也要用于描述业务规则、流程和交互。
在统一语言中,应该使用业务领域的术语和词汇,而不是技术术语和词汇。例如,应该使用“客户”而不是“用户”来描述应用程序的客户端,因为“客户”是业务领域中的术语,而“用户”则是技术领域的术语。

统一语言的重要性在于,它能够帮助开发人员更好地理解业务需求和领域模型,同时也能帮助业务专家更好地理解技术和系统实现。它还能够提高代码的可读性和可维护性,因为代码中的术语和词汇能够被业务专家和开发人员共同理解。

1.3.2 领域和子域

在研究和解决业务问题时,DDD会按照一定的业务规则将业务领域进行细分,当领域细分到一定的程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型。简言之,领域是业务领域的范围和边界。

领域可以进一步划分为不同的子领域称为子域,每个子域都包含了一些特定的业务规则和功能,它们共同构成了整个业务领域。领域和子域是基于业务需求和功能进行划分,在划分领域和子域时,需要考虑到业务需求的变化和扩展,使得系统能够更好地适应业务需求的变化。

领域和子域的划分也是一种设计上的分层,它能够帮助开发人员更好地理解和组织系统中的代码和模型。在实现时,每个子域都可以被划分成一个或多个限界上下文(Boundary Context),每个限界上下文都包含了一些特定的业务规则和功能,它们共同实现了该子域的功能。

1.3.3 核心域、通用域和支撑域

领域在不断划分的过程中,细分为不同的子域,根据重要性的不同,子域又划分为核心域、通用域和支撑域。

  1. 核心域是指业务领域中的核心业务逻辑和模型,它们是领域驱动设计的核心部分。核心域包含领域模型、领域服务、业务规则等,它们是业务领域中最重要、最稳定的部分。
  2. 通用域是指业务领域中通用的、可复用的业务逻辑和模型,它们不是核心业务逻辑,但可能在多个核心域中被使用。通用域包含一些通用的业务逻辑、数据校验、安全控制等。
  3. 支撑域是指用于支持业务领域中的核心域和通用域的基础设施和工具,包括数据存储、消息传递、分布式系统、自动化测试等。支撑域中不包含业务逻辑和模型,而是聚焦于技术实现和系统监控、管理等方面。
1.3.4 界限上下文

限界上下文是指一个业务领域的边界,它包含了多个子域(Subdomain),每个子域都包含了一些特定的业务规则和功能。限界上下文用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。

限界上下文是一个显式的边界,领域模型便存在于这个边界之内;在边界内,通用语言中的所有术语和词组都有特定的含义,而模型需要准确地反映通用语言。通过限界上下文,开发人员可以更好的组织和管理领域中的不同部分,更好的理解业务需求和领域模型。

领域、子域和限界上下文的关系如下图所示:

在这里插入图片描述

1.3.5 实体Entity

实体是拥有唯一标识和状态,且具有生命周期的业务对象。在实体的生命周期内,无论其如何变化,其仍旧是同一个实体。实体的标识和状态可以在业务过程中发生变化,而实体之间的关系也可以随着业务过程的变化而发生变化。

  • 实体的业务形态:在领域模型中实体是多个属性、操作或行为的载体。实体的属性描述实体的状态比如客户的姓名、年龄等;实体的行为指实体可以执行的操作,比如创建、更新等。实体和值对象是组成领域模型的基础单元。
  • 实体的代码形态:在代码模型中,实体表现为实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。
  • 实体的运行形态:实体是以DO(领域对象)的形式存在,每个实体对象都有一个唯一的ID,这个ID在实体的整个生命周期内无论实体对象怎么变化都具有唯一性。
  • 实体的数据库形态:DDD先构建领域模型,再针对业务场景构建实体对象和行为,最后将实体对象映射到数据持久化对象。在领域模型映射到数据模型时,一个实体可能对应0个、1个或者多个数据库持久化对象。
1.3.6 值对象

《实现领域驱动设计》一书中对值对象的定义如下:

通过对象属性值来识别的对象,它将多个相关属性组合为一个概念整体。在DDD中用来描述领域的特定方面,并且是一个没有标识符的对象,叫作值对象。值对象没有唯一标识,没有生命周期,不可修改,当值对象发生改变时只能替换。

值对象将不同的相关属性组成一个整体概念,是一堆不可变属性的集合。

  • 值对象的业务形态:值对象是若干不可修改的属性集合,只有数据初始化操作和有限不涉及数据修改的行为,基本不包含业务逻辑。值对象在逻辑上依然是实体属性的一部分
  • 值对象的代码形态:如果值对象是单一属性例如字符串、整型、枚举等,则直接定义为实体类的属性;如果值对象是属性集合,则把它设计为Class类,包含多个属性。
  • 值对象的运行形态:值对象嵌入到实体中,有两种不同的数据格式分别是属性嵌入的方式和序列化大对象的方式,比如JSON字段
1.3.7 聚合和聚合根

1)聚合和聚合根

DDD中实体和值对象是很基础的领域对象,实体一般对应业务对象、值对象一般是属性集合,但是实体和值对象都是个体化的表现。而聚合是把一些关联性极强、生命周期一致的实体、值对象放到一个聚合里。聚合根(Aggregate Root)是指聚合中的实体,它负责聚合的内聚性和一致性,同时也是聚合的入口点。

聚合有一个聚合根和上下文边界,这个边界根据业务单一职责和高内聚原则,定义了聚合内部应该包含哪些实体和值对象,而聚合之间的边界是松耦合的。按照这种方式设计出来的微服务很自然就是“高内聚、低耦合”的。

在这里插入图片描述

2)聚合设计原则

在领域驱动设计中,聚合设计是非常重要的一部分。以下是聚合设计的一些原则

  • 保持完整性:聚合中包含一组相互关联的实体和值对象,它们被视为一个整体。聚合的边界应该明确,以保持其完整性。
  • 保持一致性:聚合内数据强一致性,而聚合之间数据最终一致性。聚合中的实体和值对象在业务规则和约束下保持一致。如果一个聚合违反了业务规则或约束,那么它就不是一个一致的聚合。
  • 保持小型聚合:聚合尽可能小,只包含必要的实体和值对象。小型聚合可以降低由于业务过大导致聚合重构,有助于提高系统的可维护性和可扩展性。
  • 避免聚合根以外的实体:聚合根以外的实体应该尽可能避免,因为它们增加了系统的复杂性和耦合度。如果必须使用这些实体,应该确保它们与聚合根之间的关系是一致的。
  • 避免跨聚合引用:如果一个实体或值对象需要引用另一个聚合中的实体或值对象,通过关联外部聚合根ID的方式引用,而不是直接对象引用的方式。这样可以保持聚合的独立性和可维护性。
1.3.8 工厂

如果实体或值对象的创建过程非常复杂,可以将其委托给工厂。DDD中工厂是指用于创建领域模型对象的工厂方法,它用于将业务逻辑和实现技术解耦。它是一种设计模式,允许开发人员通过调用工厂方法来创建对象,而不需要直接在代码中实例化对象。

将创建复杂对象和聚合的职责分配给一个单独的对象,该对象本身并不承担领域模型中的职责,但是依然是领域设计的一部分。工厂应该提供一个创建对象的接口,该接口封装了所有创建对象的复杂操作过程,同时,它并不需要客户去引用那个实际被创建的对象。对于聚合来说,我们应该一次性地创建整个聚合,并且确保它的不变条件得到满足。

1.3.9 仓库

仓库(Repository)是一种模式,用于封装数据访问逻辑,提供对数据的持久化和查询。仓库操作的最小单元就是聚合,每个聚合会对应一个仓库。它旨在将数据访问细节与领域模型分离,使领域模型更加独立和可测试。

在这里插入图片描述

仓库提供了一种统一的接口,使得领域模型可以与不同的数据存储方式进行交互,同时也提供了一些查询操作。例如,一个客户仓库可以具有存储客户、检索客户等方法。通过调用仓库方法,开发人员可以存储和检索客户对象,同时也可以在存储和检索对象时执行其他的业务逻辑和验证。

1.3.10 领域服务

领域服务(Domain Service)是指跨越多个聚合或子域的逻辑服务,用于处理业务需求和行为。领域服务通常用于执行跨聚合的操作,例如处理业务规则、集成外部系统、执行复杂业务流程等。

需要注意的是,实体和领域服务在实现业务逻辑上不是同级的,当领域中的某些功能,单一实体或者值对象不能实现时,领域服务就会出马,它可以组合聚合内的多个实体或者值对象,实现复杂的业务逻辑。

1.3.11 领域事件

领域事件(Domain Event)是指业务领域中发生的一些事件,通常意味着领域对象状态的改变。领域事件在系统中起到了传递消息、触发其他动作的作用,是解耦领域模型的重要手段之一。在实现时,领域事件可以由领域模型中的实体或服务触发,例如当用户注册成功时,可以触发一个“用户注册成功”的领域事件。领域事件也可以由外部系统或服务触发,例如当第三方支付系统支付成功时,可以触发一个“订单支付成功”的领域事件。

领域事件处理包括:事件构建和发布、事件数据持久化、事件总线、消息中间件、事件接收和处理等。

在这里插入图片描述

  • 事件构建和发布:
    • 事件基本属性通常包含事件的标识符、时间戳和事件数据等信息;业务属性用于记录事件发生时刻的业务数据。事件的基本属性和业务属性一起构成事件实体。
    • 事件发布方式很多,可以通过应用服务或领域服务发布到事件总线或消息中间件;也可以从事件列表中定时获取增量事件数据发布到消息中间件。
  • 事件数据持久化:用于系统之间的数据对账,或者实现发布方和订阅方数据的审计
    • 持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性
    • 持久化到共享的事件数据库中,事件数据库和业务库是独立的。
  • 事件总线:实现微服务内聚合之间领域事务,提供事务分发和接收等服务
  • 消息中间件:跨微服务的领域事件大多会用到消息中间件,实现跨微服务的事件发布和订阅
  • 事件接收和处理:订阅方采用监听机制接收消息队列中的事件数据,当完成事件数据的持久化处理后,就可以进行下一步的业务处理
1.3.12 贫血模型和充血模型

1)贫血模型(Anemic Domain Model)

所谓的贫血模型指的是定义领域对象的时候,只有对象的属性信息,没有对象的行为信息。贫血模型强调了业务逻辑和数据之间的分离,将业务逻辑和数据分别放在不同的对象中,导致领域模型变得贫血、不灵活和难以维护。

2)充血模型(Rich Domain Model)

充血模型指的是领域对象既包括属性信息,又包含行为信息。在充血模型中,领域对象包含了业务逻辑、数据和规则,它们被组织在一个聚合根中。
相对而言贫血模型相对简单,模型上只有数据没有行为,针对简单的模型可以快速的完成交付,但是后期的成本较高,并且难以保证事务的一致性和完整性。充血模型则是领域模型模式,实现逻辑上由各自的对象负责,具备事务的一致性和完整性,事务边界也被包含在聚合根中。

1.4 DDD领域驱动设计中架构模型

在DDD领域中,常见的领域驱动设计架构有以下几种:

  1. 分层架构:将领域模型和业务逻辑分离出来,并减少对基础设施和应用层的依赖,从最早三层架构,演化到四层架构、五层架构,包括用户接口层、应用层、领域层和基础设施层。分层架构中,每层只能与位于其下方的层耦合。
  2. 六边形架构:也称为端口与适配器。对于每种外界类型,都有一个适配器与之相对应。该架构的定义涉及六个部分,分别是应用程序、域、基础设施、适配器、API和外部系统。六边形架构的核心理念是应用通过端口与外部进行交互。
  3. CQRS架构:CQRS架构是一种读写分离的架构设计,系统被分为两个部分:命令端(Command)和查询端(Query)。命令端负责处理写操作(例如修改数据),而查询端则负责处理读操作(例如查询数据)。
  4. 事件驱动架构:用于处理事件的生成、发现和处理等任务的软件架构。在这种架构中,领域对象之间通过发布和订阅事件来通信,一个事件通常会引发多个服务的同步,从而引起事件的扩散。
1.5 DDD领域驱动设计的优缺点

实现DDD的最大好处是第一时间将业务需求构建成领域模型,而不是将其切割为数据和行为,然后再用数据库去实现。DDD具有以下优点:

  • 统一业务语言:通过使用统一的领域语言,准确传达业务规则,提升团队间的沟通效率。
  • 清晰业务边界:统一各个子域的认识,通过领域模型界定需求实现边界。
  • 提升变化应对:通过领域模型与数据模型的分离,将核心业务的不变与需求的变进行有效隔离,提升架构应变化的能力。

当然使用DDD需要一定的学习和使用成本,开发人员需要掌握一定的领域建模知识和技能。同时在高并发场景下,由于DDD使用的模型可能会增加一些额外的开销。

2、DDD领域驱动设计主要架构模型介绍
2.1 分层架构

在这里插入图片描述

DDD的分层架构在不断的优化,在最早的四层架构中(如左上图所示),基础设施层被其它层依赖,处于最核心的位置,但实际上领域层才是依赖的核心。后来采用依赖倒置的设计,优化了传统的四层架构,实现了各层对基础设施层的解耦。四层架构包括:用户接口层、应用层、领域层和基础设施层。

  1. 用户接口层:负责展示用户界面和处理用户输入,它通常包含一些前端框架和UI组件
  2. 应用层:负责处理业务逻辑和应用程序的流程,它通常包含一些应用程序服务,例如事务处理、规则验证和流程管理等
  3. 领域层:领域层是领域模型的核心,它负责表示业务领域中的实体、值对象、聚合、领域服务等。领域层通常包含一些领域模型类、领域服务类和领域事件等。
  4. 基础设施层:负责提供一些通用服务和功能,例如数据存储、消息传递、日志记录等。基础设施层通常包含一些工具类、库和框架等。

在传统架构中,由于上层应用对数据库的强耦合,很多业务在架构演进中担心更换数据库对应用的影响,因为一旦更换数据库,就可能需要重写大部分代码,这对开发来说是致命的。采用依赖倒置的设计后,应用层通过解耦保持独立的核心业务逻辑,当数据库变更后,只需要更换数据库基础服务即可,对应用的影响降到最低。

2.2 六边形架构

六边形架构又称为“端口-适配器”架构,对于每种外界类型,都有一个适配器与之相对应,其核心设计思想是外界通过应用API与内部进行交互。

在这里插入图片描述

如图所示的六边形架构中,核心业务逻辑(应用程序和领域模型)与外部资源(包括Web应用、基础设施等)完全隔离,仅通过适配器进行交互。除了核心的领域模型,还包括如下三个层次:

  • 应用程序层(Application Layer):负责处理业务逻辑和应用程序的流程,它通常包含一些应用程序服务,例如事务处理、规则验证和流程管理等。
  • 适配器层(Adapter Layer):负责与外部系统进行交互,例如与数据库、消息队列、Web 服务等进行通信和交互。它通常包含一些适配器类和工具类等。
  • 基础设施层(Infrastructure Layer):负责提供一些通用服务和功能,例如数据存储、消息传递、日志记录等。基础设施层通常包含一些工具类、库和框架等。

在六边形架构中,一个端口可能对应多个外部系统,不同的外部系统也可能会使用不同的适配器,由适配器负责协议转换。因此在使用时,根据用例来设计应用程序,所有的适配器使用相同的API满足不同的端口访问需求。

2.3 CQRS架构

CQRS是一种读写分离的架构设计,系统分为两个部分:命令端和查询端,命令端负责处理写操作,而查询端负责处理读操作。在CQRS架构中,写操作和读操作被分离并分布在两个独立的系统中。命令端和查询端之间的数据一致性采用最终一致性的方式处理。这意味着查询端可能会返回不是最新的数据,但系统会确保在一定时间内数据达到一致状态。

在这里插入图片描述

除了分离写操作和读操作,CQRS架构还涉及到事件的概念。事件表示命令操作领域中的聚合根状态发生变化后产生的事件。这些事件可以被其他服务订阅,以实现系统的联动。

3、DDD领域驱动设计落地实践

前面介绍了DDD的基本概念以及主要架构,这些概念很抽象,读起来是一头雾水。下面以“在线书店”为例,探讨下DDD具体落地实践。

3.1 战略设计

在战略设计阶段,主要过程分为业务场景分析、领域建模、划分边界上下文,战略设计是DDD中的核心步骤。

3.1.1 业务场景分析

业务场景分析阶段是对业务的全面梳理,将业务中涉及到的细节展现出来,为后续的领域建模提供足够的业务输入。常见的业务场景分析方法有事件风暴法、四色建模法、用例分析法等,四色建模法相对比较复杂,本文以事件风暴法对“在线书店”业务场景进行分析梳理。

1)场景分析参与方

事件风暴类似于头脑风暴的方法,业务场景的分析讨论参与角色包括领域专家、产品经理、需求分析人员、架构师、项目经理、开发经理和测试经理。参与者要尽可能遍历所有业务细节,列出具体的业务场景、业务规则、业务之间的关联以及热点问题等。

2)业务讨论与梳理

经过事件风暴讨论后,总结出“在线书店”下的典型的业务场景,包括:

  • 书籍管理:核心业务场景是书籍销售,书籍管理包括书籍信息的录入、编辑、查询、分类、推荐等。
  • 用户管理:为用户提供注册、登录、个人信息管理、购物车管理等功能。
  • 订单管理:为用户提供订单的创建、支付、发货、跟踪等功能。
  • 支付管理:为用户提供安全的支付方式

分析业务中的事件,寻找业务逻辑和业务规则,比如用户购书流程,从用户注册登录、书籍查询推荐、订单创建和支付等流程进行梳理。下面是按照业务操作对象、事件和命令(动作)形成事件风暴小黑板对整个业务流程进行梳理。

在这里插入图片描述

3.1.2 领域建模

在领域建模时,根据场景分析过程中产生的领域对象,找出产生命令的实体,分析实体之间的依赖关系产生聚合,为聚合划定界限上下文,建立领域模型以及模型之间的依赖。根据上图业务场景分析,提取产生这些行为的实体,包括用户、订单、书籍、购物车和系统管理。

在这里插入图片描述

完成领域对象分析后,需要构建业务聚合,首先在实体中找到聚合根,根据聚合根的特点,聚合根是一个实体具有全局唯一标识,并且具备生命周期的同时需要专门的模块进行管理。根据这些特征就有了书籍、用户、订单和系统管理这些聚合。

3.1.3 划分边界上下文

获得了整个业务流程中的所有聚合后,需要更具业务语义上下文将具体的聚合划分到对应的上下文中。根据订单和购物车的业务操作行为,订单和购物车共同构成订单域分别管理订单和购物车,用户管理和系统管理共同构成系统和用户管理,因此分为订单域、商品域及系统和用户管理域三个限界上下文。

在这里插入图片描述

3.2 战术设计
3.2.1 微服务拆分

通常一个限界上下文拆分为一个微服务,微服务的设计还需要考虑服务的粒度、分层、边界划分、依赖关系和集成关系。因此,在“在线书店”业务场景中,拆分为订单服务、商品服务和系统用户管理服务三个微服务。

3.2.2 领域分层

根据前面介绍的四层分层架构,对领域进行分层设计,包括:用户接口层、应用层、领域层和基础设施层。

//用户接口层
+-- UI接口

// 领域层  
+-- Entity (实体)  
+-- Value Object (值对象)  
+-- Domain Service (领域服务)  
+-- Domain Rule (领域规则)  
+-- Domain Event (领域事件)  
+-- Resource Library (资源库)  
  
// 应用层  
+-- Application Service (应用服务)  
+-- Application Event (应用事件)  
+-- Application Command (应用命令)  
+-- Application Query (应用查询)  
  
// 基础层  
+-- Data Access Object (数据访问对象)  
+-- Repository (仓储)  
+-- Service Location (服务定位)  
+-- Logging (日志记录)  
+-- Data Store (数据存储)
3.3 代码实现

1)定义书籍实体,它具有书名、作者、出版日期和价格等属性

class Book:  
    def __init__(self, title, author, publication_date, price):  
        self.title = title  
        self.author = author  
        self.publication_date = publication_date  
        self.price = price

2)定义一个值对象类ISBN,它代表书的唯一标识符

class ISBN:  
    def __init__(self, value):  
        self.value = value

3)定义一个领域服务类BookstoreService,它包含一些业务逻辑,例如添加书籍、查找书籍和下架书籍等

class BookstoreService:  
    def add_book(self, book):  
        # 在这里添加书籍到数据库或者其他数据存储中  
        pass  
  
    def find_book_by_title(self, title):  
        # 在这里根据书名查找书籍  
        pass  
  
    def remove_book(self, isbn):  
        # 在这里根据ISBN删除书籍  
        pass

4)定义一个领域事件类BookEvent,它代表书籍状态的变化,例如书籍上架、书籍下架等:

class BookEvent:  
    def __init__(self, book, event_type):  
        self.book = book  
        self.event_type = event_type

5)定义一个应用服务类BookstoreApplicationService,它与用户交互,并调用领域服务类中的方法来实现业务逻辑

class BookstoreApplicationService:  
    def __init__(self, bookstore_service):  
        self.bookstore_service = bookstore_service  
  
    def add_book(self, title, author, publication_date, price):  
        book = Book(title, author, publication_date, price)  
        self.bookstore_service.add_book(book)  
        book_event = BookEvent(book, "added")  
        # 在这里发布书籍事件  
        pass  
  
    def find_book_by_title(self, title):  
        book = self.bookstore_service.find_book_by_title(title)  
        if book:  
            return book  
        else:  
            raise ValueError(f"Book with title '{title}' not found")  
  
    def remove_book(self, isbn):  
        book = self.bookstore_service.remove_book(isbn)  
        book_event = BookEvent(book, "removed")  
        # 在这里发布书籍事件  
        pass

以上是Python实现一个简单的DDD项目示例,它包含了实体、值对象、领域服务、领域事件和应用服务等多个组件。

4、总结

本文简要介绍了DDD领域驱动设计的基本概念、常用的分层架构,并结合实际业务场景落地DDD设计实现。总而言之,DDD是一种软件设计的思想和方法论,其核心是将业务抽象成领域模型,并以领域为核心驱动力构建软件设计体系,将复杂的业务模型抽象化,非常适合微服务架构下的开发设计。


参考资料:

  1. 《实现领域驱动设计》,Vaughn Vernon著
  2. 《解构领域驱动设计》,张逸著
  3. 领域驱动设计DDD|从入门到代码实践,阿里云开发者
  4. https://blog.youkuaiyun.com/m0_73311735/article/details/127037315
  5. https://blog.youkuaiyun.com/m0_37583655/article/details/117565641
  6. DDD兴起的原因以及与微服务的关系
  7. 2万字带你入门DDD,芋道源码
  8. DDD 领域驱动设计落地实践:六步拆解DDD,慕枫技术笔记
DDD领域驱动设计详解 领域驱动设计是Eric Evans在2004年发表的Domain Driven Design(领域驱动设计,DDD)著作中提出的一种从系统分析到软件建模的一套方法论。以领域为核心驱动力的设计体系。从领域驱动定义来看,领域驱动设计-软件核心复杂性应对之道,从Eric 定义中可以看出,领域驱动设计是为了解决复杂的软件设计,而且只是解决软件复杂性的一种方式,并不是唯一选择。另外不是所有的业务服务都合适做DDD架构,DDD适合产品化,可持续迭代,业务逻辑足够复杂的业务系统,对于系统初期业务逻辑相对比较简单的应用,传 阅读详情

相关推荐

架构师篇 DDD领域驱动设计篇

领域驱动设计(英文:Domain-Driven Design,缩写DDD)是一种模型驱动设计的方法,领域驱动设计常以战略设计与战术设计来将整个领域展现的淋漓尽致,其作用范围既面向业务也面向技术。从战略角度(个人更喜欢称其为上帝视角)去规划系统、划分领域。而从战术角度则从技术层面来指导我们该如何去设计。功能作用:1.通过模型直接反映软件的结构;2.以模型为基础形成团队的统一语言;3.把模型作为精粹的知识用于传递。领域驱动设计的核心在于领域建模,架构师的水平高低在很大程度上也体现在领域建模水平上。对于业务逻辑不

健康平安的活着的专栏 1429

DDD(领域驱动设计) 概述

DDD 领域驱动设计 概述

qq_36010886的博客 1万+

带你入门领域驱动设计(DDD)

带你入门领域驱动设计(DDD)一、DDD的战略模式二、DDD的术语三、DDD的分层架构四、总结 要讲DDD,不得不先说一下微服务,因为2004年由Eric Evans提出的DDD就是伴随微服务的兴起才被人们重视起来的。 微服务是一种架构,它将传统的单体架构进行拆分,使系统可以分而治之、相互解耦。我们享受微服务带来的便利的同时,也不得不直面随之而来的困惑。比如,如何拆分服务?微服务架构理论工具大都从技术角度考虑,比如服务间调用、分布式、系统运维等。当业务需求迭代的时候,却不能很好地支撑,甚至造成一个简单需求需

zybank_IT的博客 1826

DDD架构理论详解

DDD是领域驱动设计(Domain-Driven Design)的缩写,这是一种主要软件开发方法,由Eric Evans在它的书《领域驱动设计:软件核心负责性应对之道》中首次提出。DDD主要关注于创建与业务领域紧密相关的软件模型,以确保能够准确地解决实际问题。

qq_43456605的博客 2万+

初识DDD-理解基本概念

0 概述 2004 年埃里克·埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design –Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design,简称 DDD)诞生。领域驱动设计这一理念迅速被行业采纳,时至今日仍是绝大多数人进行业务建模的首要方法。随着Martin Fowler 提出微服务架构,DDD也迎来了新的时代。 DDD概念比较多,像:领域、子域、核心域、通用域、

huangshanchun的专栏 1828

DDD软件架构领域驱动设计

目录 1. DDD概述 1.1 软件开发的困境 1.2 DDD的来源及简介 1.2.1 DDD设计方法 1.3 DDD解决了什么问题 1.3.1 沟通问题 1.3.2 代码质量问题 1.4 模型和建模 1.4.1 什么是模型 1.5 统一语言(UBIQUITOUS LANGUAGE) 1.6 什么是DDD 2. 传统开发模式 2.1 基础知识回顾 2.1.1 面向对象编程 2.1.2 MVC架构 2.1.3 贫血模型 2.1.4 充血模式 3. 转账业务 3.1 业务流程 3.1.1 业务分析 3.2 传统

赵广陆 815

领域驱动设计(DDD)基本概念的自我理解

DDD个人主要理解 它微服务的设计思想 主要由子域与边界上下文组成 子域:领域驱动为每一个子域设计独立的领域模型,每一个子对应着一个业务 边界上下文:子域的边界 它是对面向对象设计的增强 与数据驱动的区别 数据驱动:我们传统的代码架构是dao、service、controller三层组成,大多数的业务本质是对数据库表的增删改查(比如开发一个简单用户注册系统) 数据驱动的痛点: (1) 存在接口定义不狗清晰(参数定义不够清晰,比如都用string类型去接收) (2) 存在业务代码不够清晰(比如核心业务代码中

皇家程序猿 538

DDD 领域驱动设计的基本概念 个人理解

聚合内的通过领域服务去处理,根据是否变实体的状态可进一步划分为查询类、增删改的事件领域服务,业务复杂可以再进一步用策略设计模式去创建更多不同的领域服务类。像是springMVC的映射层、MQ之类的还是最上层的用户接口层、第三方的接口属于获取数据又不影响本领域实体的状态所以属于基础层等等。3). 实体的划分,聚合、聚合根、上下文界限的划分来确定代码上划分实体等,通过事件风暴推断出可能存在的领域模型和聚合之间的联系等。2). 实体之间的关系可由UML图等建模图形去处理更好,图形划分领域和资源之间的关系。

入门菜鸟程序员的成长 511

C#进阶系列——DDD领域驱动设计初探(一):聚合

本文转载自:http://www.cnblogs.com/landeanfen/p/4816706.html 作者:landeanfen 转载请注明该声明。前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的。今天看到一篇博文里面写道:越是忙人越有时间写博客。呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西。前面介绍了下MEF和AOP的相关知识...

zhengxiuchen86的博客 1122

混合开关电容DCDC变换器技术解析与工程应用指南

在电源设计领域,传统DCDC变换器在高降压比或高升压比场景下,往往面临占空比过窄、开关应力大、效率偏低等瓶颈。开关电容变换器虽能实现固定比例的电压变换,却受限于调节能力。混合开关电容DCDC变换器通过“开关电容网络粗调变比+电感式DCDC细调输出”的策略,兼顾高效率、高功率密度与宽调压范围。本文梳理了其典型拓扑(如2:1/4:1 SC+Buck)、关键对比、控制策略(含软充电)及工程应用中的常见问题,帮助电源工程师在服务器供电、快充、车载电源等场景下快速选型与调试。

weixin_29164185的博客 311

【仓颉 + 鸿蒙 + AI Agent】CangjieMagic框架(3):Agent请求和响应

CangjieMagic框架:使用华为仓颉编程语言编写,专门用于开发AI Agent,支持鸿蒙、Windows、macOS、Linux等系统。这篇文章剖析一下 CangjieMagic 框架中的 AgentRequest 和 AgentResponse。

九丘教育官方博客 1475

深入讲解领域驱动设计(DDD)架构

领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,旨在解决复杂软件系统中的业务问题。它强调通过紧密结合领域专家和开发团队,构建高质量的软件模型,以便更好地理解和实现业务需求。DDD由Eric Evans在其著作《Domain-Driven Design: Tackling Complexity in the Heart of Software》中提出,强调通过领域模型的构建和维护,使得软件系统能够更好地反映业务逻辑和需求,从而提高系统的可维护性、可扩展性和可理解性。

lssffy的博客 1094

librealsense2 camera.so: undefined symbol:ZN2cV3MatC1EV解决笔记记录

先参考的这个兄弟的办法虽然没解决但是提供了大方向感谢; realsenseD400系列相机报错:undefined symbol: _ZN2cv3MatC1EiiiRKNS_7Scalar_IdEE解决方案 后解决办法参考 No GPU usage on jetson xavier NX · Issue #2326 · IntelRealSense/realsense-ros · GitHubOpenCV_DIR" to a directory containing one of the above f

梁俊星的博客 3513

浅析 DDD 领域驱动设计

前言 最近公司一场有关于领域驱动设计的技术分享会,引起了我的注意,主要讲解了服务的划分,Restful API的设计,如何将抽象具有统一业务的范畴的Model,使其模块化,同时能够提炼组合多个模块,使得业务能够独立服务化,在软件开发中如何降低系统的复杂度是一个永恒的挑战,在之前都是通过一系列的设计模式或者范例来降低一些比较常见的复杂度,这些都是通过技术手段来解决技术问题,没有从根本上解决业务上的问题,但是在03年 Eric Evans 的《Domain Driven Design》 中,才是真正的从业务的

牧小农 3960

VConsole动态加载

// 移动端真机调试工具 vconsole import VConsole from "vconsole"; import Vue from "vue"; // 动态使用VConsole if(process.env.NODE_ENV==='production'){ if(process.env.VUE_APP_BASEURL.includes('test')||process.env.VUE_APP_BASEURL.includes('pre')){ Vue.prototype

aliven1的博客 1041

单片机笔记十二:华大HC32L136超低功耗设计实战

本文详细介绍了华大HC32L136单片机的超低功耗设计实战,包括深度睡眠模式下仅0.5μA的电流消耗、五种功耗模式的对比与配置、外设使用技巧及硬件级优化方法。通过实际项目案例,展示了如何利用ARM Cortex-M0+内核的HC32L136显著延长电池寿命,适用于智能门锁、额温枪等低功耗设备。

weixin_29189373的博客 387

欢迎来到领域驱动设计(DDD)项目

本项目旨在为领域驱动设计(Domain-Driven Design,简称DDD)提供一个易于理解的起点。它包含了DDD的基本概念定义,旨在降低学习曲线和减少困惑。DDD是一种软件开发方法,它强调的是基于业务领域的复杂性来进行软件设计。 ## 2. 项目快速启动 为了帮助您快速上手本项目,以下是启动的基本步骤: 首先,您需要克隆项目到本地环境: ```sh git clone https:/...

gitblog_00505的博客 442

【maven】maven 3.8.1 版本编译java 17的项目报错 java.lang.ExceptionInitializerError to make field private com.s

排查发现一切正常,但是内网就是不行,然后我就想把我的程序从java 11 改成java 17,然后执行编译的时候报错。这个问题是我们内网java代码升级,项目比较复杂,是从java 8 升级到java 17,然后报错。maven-compiler-plugin 3.8.1 版本编译java 17的项目,报错。然后查看一下这个项目的lombok,结果发现还是老的版本。怀疑是不是这个问题呢?使用最新的版本就可以了。

九师兄 780

商品领域ddd_DDD领域驱动设计实战详解(一 核心概念)

设计不只是感观,设计就是产品的工作方式。——Steve Jobs 相信很多小伙伴,或多或少都会对DDD,有过一定的了解,但总是感觉像天书一样,不知道如何下手,或许也看过很多关于 DDD 的文章, 也买过一些书籍, 但是发现内容冗长, 同时讲解的也不够系统,导致我们的知识体系总是零散的,无法串联起来,在遇到实际项目时 不知道该如何入手,由于 DDD 不是一套框架,而是一种架构思想,所以在...

weixin_39600319的博客 977

STM32CubeIDE下载程序报错:Failed to execute MI command: load G:\\Cube_IDE_EXP\\00_FIRST\\Debug\\00...如何解决??

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 2592

电磁场仿真:电磁场基础理论_(13).微波技术基础

微波是指频率在300 MHz到300 GHz之间的电磁波。这一频率范围对应的波长为1米到1毫米。

2401_87715305的博客 1314

浅谈我对DDD领域驱动设计的理解

从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决。 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品。所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的。 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备。但是最近由于各种原因,导致服务经常出故障。所以,我们希望通过各种措施提高服务的质量和稳定性。其中的一个措施就是希望能做一个灰度发布的平台,这个平台可

骏马逸动,心随你动的博客 532

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

ddd领域驱动java_DDD- 领域驱动设计入门

概述去年4月份入职后第一次了解到DDD,当时觉得很晦涩(现在来看应该是红皮书将战略设计放在前半部分的缘由)。今年4月份的时候想学学系统设计方面的知识,便开始静下心来好好研究了两个月,感觉收益颇多。DDD是什么DDD是一种'面向对象'的软件设计思想。领域驱动设计DDD作为一种软件设计方式, 有利于创造一个可测试的、可伸缩的、组织良好的软件模型贫血症与失忆症写Java差不多刚好两年, 接触过的项目中,...

weixin_30431043的博客 516

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

领域驱动设计学习:领域、子域、限界上下文

最近在看Vaughn Verron著的《实现领域驱动设计》这本书,对于以前只接触过MVC,没接触过DDD、微服务项目的我来说,学习DDD(Domain-Driven Design)算是一种设计思想上的跳跃了,书中的概念和概念之间的关系理解起来比较吃力,因此就很有必要写篇文章梳理一下了。 我首先画了个思维导图表示大小概念之间的关系: 图1:思维导图01 1、领域 这里引用北理工金旭亮老师...

实用的博客 4539

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

DDD 领域驱动设计理论知识总结

这篇文章分享自己陆续学习的 DDD 相关的概念,持续更新中,欢迎阅读斧正。

格物致知,知行合一。 960

DDD-领域驱动设计

领域驱动设计的核心是“领域”,用正确的方法做正确的事情,运用领域驱动设计,就是要先识别问题域,进而为团队提炼达成共识的领域知识。要做到这一点,离不开团队各个角色的沟通与协作。

youdingte的博客 1374
上一篇: 低代码开发平台介绍
下一篇: 分布式版本控制系统Git介绍
solihawk
博客等级 码龄17年 7995粉丝 184原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值