惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
爱范儿
爱范儿
Recorded Future
Recorded Future
博客园 - 【当耐特】
博客园 - 聂微东
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
C
Check Point Blog
N
Netflix TechBlog - Medium
阮一峰的网络日志
阮一峰的网络日志
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
罗磊的独立博客
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
The Cloudflare Blog
博客园 - 司徒正美
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
J
Java Code Geeks
B
Blog
Martin Fowler
Martin Fowler
L
LangChain Blog
V
Visual Studio Blog
U
Unit 42
P
Proofpoint News Feed
Vercel News
Vercel News
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
F
Fortinet All Blogs
M
MIT News - Artificial intelligence
人人都是产品经理
人人都是产品经理
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
The Register - Security
The Register - Security
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
T
Tailwind CSS Blog
H
Help Net Security
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell

顾宇的博客

用 AI 重启生活 初始化 MacOS 关于我 再次见面,期待相遇 演讲和分享 如何通过政策红利获利?——海南发展(SZ.002163)交易复盘 2025年的总结 2024年的总结 2023年的总结 2022年的总结 《敏捷测试价值观、方法与实践》序 【翻译】通过跟踪技术债来改进你的开发实践 【翻译】不要把测试用例自动化 【翻译】持续部署 vs 持续交付 【翻译】测试替身 【翻译】做多少测试才足够 【翻译】蓝绿部署的起源 【翻译】持续部署 【翻译】作为演进式架构的微服务架构 【翻译】gRPC 的动机和设计原则 【翻译】分布式计算谬误 【翻译】微服务和分布式对象第一法则 采用 Multipass 管理本机虚拟 K8S 集群 博客主题升级到 Congo 2.0 【翻译】Terraform 最佳实践:模块组合 【翻译】Kubernetes 部署语言(Kubernetes Deployment Language) 通过 Vagrant 一键初始化 K8S 集群 2021年的总结 通过 Github Actions 部署 Mkdocs 文档 博客迁移到了新的 Hugo 主题 2020年的总结 2019年的总结 千人规模组织级 DevOps 演进的 9 个实践技巧 从技术雷达看 DevOps 的十年——容器技术与微服务 DevOps 模式 - 引入 DevOps 顾问 DevOps 模式 - 索引 DevOps 模式 - 定义你的DevOps 从技术雷达看 DevOps 的十年——基础设施即代码与云计算 DevOps 模式 - 采用模式语言讨论 DevOps 从星巴克店面运营学习 DevOps 从技术雷达看 DevOps 的十年——DevOps与持续交付 【翻译】微服务安全:所有应该被问到的问题 云原生下的 DevSecOps 实践 【翻译】软件定义交付宣言 微服务演进中的经验和反思 迟到的 2018 年终总结 成功微服务实施的组织演进 从第19期技术雷达看 DevOps 的发展趋势 成功微服务实施的技术演进 我们如何衡量微服务的成功? 关于四周四 AWS 架构工作坊的设计和实践 讨论微服务之前,你知道微服务的 4 个定义吗? 公有云(AWS)上的生产环境架构优化案例和迁移套路总结 公有云(AWS)上的生产环境性能分析案例 一怒之下,我又写了一个开源流量测试工具 采用 DevOps 故事落地 DevOps 测试驱动开发 Nginx 配置 云原生 DevOps 翻译-混沌工程的原则 Serverless 风格的微服务的持续交付 从最新一期技术雷达看 DevOps 的发展 关于 DevOps ,咱们聊的可能不是一回事 Serverless 风格的微服务的架构案例 提升微服务实施效率的 7 个步骤 微服务实施常被忽视的 5 个难点 你的 CI 在挖矿吗? DevOps前世今生 - 4. DevOps 的文化 DevOps发展的九个趋势 不要让你的持续集成服务器成为安全隐患 DevOps 前世今生 - 3. DevOps 的目标和核心 DevOps 前世今生 - 2. DevOps 矛盾从何而来 DevOps 前世今生 - 1. DevOps 编年史
【翻译】函数式编程中的领域驱动设计
顾宇 · 2022-07-27 · via 顾宇的博客
  1. 顾宇的博客/
  2. Blogs/
  3. 【翻译】函数式编程中的领域驱动设计/

原文:https://www.thoughtworks.com/insights/blog/architecture/domain-driven-design-in-functional-programming

领域驱动设计 (DDD) 提供了许多技术和模式来控制软件应用程序中的复杂性——即使这些是用函数式编程语言编写的。 不幸的是,用函数式编程语言实现 DDD 可以参考的资源非常有限。 即使你设法找到了它,它也常常缺乏函数式编程的实质。

因此,DDD 通常被认为只适用于面向对象的编程。 例如,就有人会认为,函数式语言默认使用不可变(immutable)的数据结构,因此可以抛弃来自领域驱动设计的许多想法。

虽然状态不可变会使得影响状态的代码更加可见,但最终结果仍然是多段代码直接影响全局的状态(例如可能存储在数据库中)。 当然,副本从一个函数传递到下一个函数,但仍然存在一个“当前”状态,让一切直接失去控制。

在某种程度上,问题不在于状态的可变性,而在于它的所有权。谁负责保持状态内部的一致?

领域驱动设计提供了一组模式来解决许多这样的问题。在这篇文章中,我们将探讨如何让领域驱动设计适合函数式编程语言。

战略模式 vs 战术模式 #

战略模式和战术模式

领域驱动设计(DDD)分为战略模式和战术模式。 战略模式由限界上下文、通用语言和上下文映射等模式组成; 战术模式由值类型、实体和聚合等模式组成。

战略模式很容易映射到任何语言。 它们主要涵盖更高级别的软件设计,例如有界上下文、上下文映射、反腐败层、有界上下文集成模式。 这些模式不依赖于所使用的编程语言或框架。

然而,战术模式依赖于编程语言结构和范式。 我们将进一步探讨如何在函数式语言中应用这些战术模式中的一些,而不会失去函数式编程的真正本质。

聚合 #

领域模型

聚合背后的想法是强制一致性和不变量(invariants)。聚合是强制执行不变量并充当一致性边界的地方。当更新聚合的一部分时,可能还需要继续更新其他部分以确保其一致性。

在从面向对象 (OO) 映射函数式编程 (FP) 中的聚合等概念时,我曾有一个误解,那就是只考虑因为数据和行为在 OO 中总是共存的。 但是,在 FP 中,你会倾向于将数据和函数分开。

通用语言不仅是任何领域名词的集合,而且是动词、过程和约束的集合。 名词对应数据结构,动词对应领域中的操作。 识别动词也是一个重要部分,因为它决定了哪个操作应该在其领域中。

值类型和实体在函数时编程中的区别 #

经典的 DDD (面向对象的)实现基于它们的可变性和唯一性概念来区分值类型和实体类型。 值类型是不可变的,它们本身不能传达足够的信息,例如,颜色可能是一种值类型,其中颜色类型本身没有任何意义,但是当附加到像衬衫或汽车这样的实体时(例如红色 衬衫或黑色汽车)就在领域中有了意义。

相反,实体具有生命周期。 这些是可变的类型,并通过不同的生命周期事件变化。 例如,订单可以是经历不同生命周期事件的实体,例如添加到订单的商品或从订单中删除的商品。 每个生命周期事件都会改变实体。

在函数式编程中,默认情况下一切都是不可变的,这导致我们错误地认为不需要区分值类型和实体。 但是值和实体类型的概念是基于领域模型的生命周期的,因此同样可以应用在函数式语言中。

建模聚合 #

当应用程序增长时,你最终可能会对数据库分区或使用分布式数据库,这意味着曾经存在于同一台机器上的实体/聚合现在存在于不同的机器上。关于代码库中实体位置的任何假设可能不再有效; 在单个事务中更新多个实体的任何尝试都将进入分布式事务的不稳定领域。 因此,要避免这些陷阱,请遵循以下三个准则。

  1. 聚合作为事务边界:每个聚合用作事务边界。 这个唯一标识的聚合是事务的范围,不要尝试将多个聚合放在一个事务范围中,因为如果这些聚合移动到不同的机器,你无法保证事务的成功。

  2. 消息用于聚合:无论您是构建微服务还是单体应用程序,你都不应该对其他聚合的位置做出任何假设。每个聚合通过向其地址发送消息与另一个聚合进行通信 — 通过聚合的唯一ID。

  3. 聚合表示不相交的数据集:不要因为它们看起来相同或方便就让不同的聚合共享模型。不要构建持久层来连接这些不相交的聚合。不要为了遵循 DRY(Don’t Repeat Yourself)就创建一个库来共享来自不同聚合的模型。而是需要更多的思考,以确保这些聚合代表一组不相交的数据。

以下是一些领域驱动设计中常用的函数式编程模式:

  1. 采用 Lens 更新聚合:在函数式编程中,更新深度嵌套的聚合可能很麻烦,因为数据是不可变的。 这就是 Lens 发挥作用的地方。Lens 允许您更新深度嵌套的值,并获取整个更新后的聚合。
  2. 使用 Monoid 来表示值对象:本文档很好地解释了 DDD 上下文中的 Monoid。
  3. 使用基于属性的测试来测试领域不变量。
  4. 如果想更炫,使用 Reader Monad 进行依赖注入。
  5. 通过遵循命令式外壳和函数式核心模式或使用 Free Monad,将副作用保持在边缘。

DDD 设计原则似乎与一些函数式编程的良好实践相冲突,但它是对复杂业务领域进行建模的重要工具。 我认为关键是理解 DDD 模式的本质,然后找到合适的构造/抽象来表示它们。

(完)