nextjs上的DDD架构

背景

新入职公司,需要快速把之前杂乱无章的首页(有复杂业务,nextjs)搭一个靠谱的架构,否则基本没办法把事情继续推进了(核心流程需要持续大量适配到不同的后端实现上)。
个人客户端出身,之前落地DDD都是在正经强类型、静态类型语言上,而nextjs(ts)上语言习俗与DDD模式格格不入,遂制定了一些ts友好的规则来落地DDD。

DDD与ts

DDD的核心组成是充血对象(entity)+immutable 对象(vo)+整合服务(domain service/aggregate)。当然还有领域、限界上下文这种更抽象的原则。
非常非常OOP的理念,希望利用类结构建模真实世界。而且经常会利用多态来做类型行为变化,进而很容易做适配。
BUT,ts世界里,经过this的暴虐和hooks的大行其道,类、多态、实现接口都是异类。

落地

核心目的是让nextjs里能用原汁原味的ts写出来原教旨的DDD。同时,达成高效适配不同后端和高效的前后端统一话术。

文件夹结构

nextjs的一级目录默认是按技术分类的,这个是为了框架的实现成本。但是,既然引入DDD,那么除了方便框架的api和pages,其他目录一定是按业务的架构来组织。绝对不能按技术持续划分下去。

|-api
|-pages
|-domain0|- context0|- entity0.ts|- entity1|- entity-p0.ts|- entity-p1.ts|- service0.ts
|-domain1
...

充血模型

充血模型的核心是文件级别的solid,数据与行为在同一个文件中。具体业务的迭代会只发生在这个文件中。换句话说,与这个模型相关的知识仅存在于这个文件中。当然,为了保持文件长度可控,这个“文件”也可能是一个文件夹+一个index文件。

几个细节要注意:

  • 多态是靠factory产生不同对象来实现的
  • 而这些伪多态方法的第一个入参必须是self
  • 虽然entity是mutable的,但是entity对象仍然保持immutable,所有变化都返回一个新对象

entity.ts

export interface SomeEntity {someProp: string;yaProp: number;somePloyFunc(self: SomeEntity);
}export const someFunc = (self: SomeEntity): SomeEntity => {// some logicreturn {...self,//mutate some thing}
}

factory.ts

import {implType1} from 'adapter1'
import {implType2} from 'adapter2'export const someEntityFactory = (input: any): SomeEntity => {const { type } = input;if (type === type1) {return {...input,somePloyFunc: implType1}} else if (type === type2) {return {...input,somePloyFunc: implType2}} else {throw 'unknown type'}
}

VO也可以用差不多的逻辑,但是由于其immutable和用后即抛的特质,interface应该就足够了。

领域服务

普通领域服务其实就是取好名字,export一个function就好了,入参就是entity和VO。
但是,涉及到多实现适配就很难用interface+impl的方式实现了。这里要仿照react的useProp来处理。这种逻辑一定是有三部分组成的:标准的整体流程调度,不同的具体实现细节和实现细节间共享的逻辑。

export const simpleService = (a: Entity1, b: Entity2):Entity3 => {
...
}export const useAdaptedService = (a: Entity1, b: Entity2, someThingToAdapt:((a: Entity1)=>Entity3))=> {
// common logicconst c = someThingToAdapt(a)
// more common logic
}export const commonLogic = (a: Entity1, b: Entity2):Entity3 => {}

Aggregate 同理,只是先聚合了一些实体再提供服务。

前后端同构

nextjs这种框架非常好的提供了前后端同构的机会,特别是再利用tRPC抹平网络请求的话,同构会非常舒服。而且这种同构天然符合DDD的领域和限界上下文的理念,无成本的让相同的命名、行为在不同的端上复用。
以entity举例来说,一个entity一定会有属性和方法。这两部分都会有同构(业务的核心复杂度)和异构(前后端各自的偶然复杂度)的地方。那么文件结构和代码应该如下组织:
some-entity/entity.ts

export interface SomeEntity {coreProp: string;coreFunc1(self: SomeEntity);
}export const coreFunc2 = (self: SomeEntity) => {}

some-entity/fe/entity.ts

export type SomeEntityFe = SomeEntity & {feProp: number;feFunc1(self: SomeEntityFe);
}export const feFunc2 = (self: SomeEntityFe) => {}

some-entity/bff/entity.ts 与fe类似。
其中的coreFunc1的前端实现是请求后端,后端的实现是真正的业务逻辑,靠tRPC桥接。

前后端异构

还有一波前后端异构的部分是api和react component的实现。这些只有一个要求:除了最外层的整合,都放到domain下。这样,domain可以认为是完美闭包的,复用和导出的成本为零,迭代时做权限管理也只需要关注domain下的路径:前端负责domain//fe/的代码,bff负责domain//bff/,业务架构师负责domain目录下其他部分。
api/domain/entity/some-action.ts

export const handle = (req) => {const a: Entity1Bff = entity1Factory(req);const b: Entity2Bff = entity2Factory(req);const imp = req.xxx?imp1:imp2;const result = useAdaptedService(a, b, imp);return Response();
}export const POST = handle;

tsx同理。

总结

nextjs的同仓开发能带来非常好的领域/限界上下文代码共享能力。再利用好factory和typedef,可以以领域为维度组织起一整套不论是DDD还是ts视角都很合理的架构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【java】22:try-catch 异常处理

try-catch 方式处理异常说明 public static void main(String[] args) { int num1 10; int num2 0; try { int res num1 / num2; } catch (Exception e) { System.out.println(e.getMessage()); } } 注意事项 1)如果异常发生了,则异常发生后面的代码不会执行&…

AWK命令详解: 源于UNIX的强大文本处理神器

AWK,1977年由Alfred Aho,Peter J. Weinberger和Brian Kernighan共同在贝尔实验室创建,是一个强大的文本分析工具,跨越了几十年的发展,一直在UNIX和类UNIX系统中广为使用。 AWK的功能理解 AWK主要用于在文本文件中搜索…

前后端分离项目Docker部署指南(下)

目录 前言: 一.安装nginx 创建目录 上传nginx.conf至/data/nginx/conf文件夹中 运行启动容器 上传静态资源文件 ​编辑 访问结果 前言: 在上一篇博客中,我们深入探讨了如何使用Docker部署一个前后端分离的项目中的后端部分。我们构建…

基于qt的图书管理系统----05其他优化

参考b站:视频连接 源码github:github 目录 1 优化借阅记录显示2 时间显示为年月日3 注册接口 1 优化借阅记录显示 现在只能显示部分信息,把接的书名和人的信息全部显示 在sql语句里替换为这一句即可实现查询相关联的所有信息 QString str…

单例九品--第七品

单例九品第七品 上一品引入写在前边代码部分实现思路的评注与思考下一品的设计思考 上一品引入 第六品着重解决了因为链接顺序造成的未定义问题,通过强制对象完成编译期初始化和使用基本类型代替抽象类型,使得全局对象的缺省初始化从不平凡变为平凡初始…

全量知识系统问题及SmartChat给出的答复 之18 三个子系统 之1

Q56.全量 知识系统中的三个子系统 下是全量知识系统的三个子系统的内部需要的内容。请仔细阅读上述内容,先设计一段程序能表示上述信息中你能了解到的部分。 1、数据系统{ projection-语法 key-value }里程牌:数据及数据类型 区划技术板块:…

2024 年 AI 辅助研发发展与趋势研究

引言 这几年,人工智能(AI)技术火得不行,它渗透到了我们生活的方方面面。从帮助我们识别图片、理解语音,到推荐我们喜欢的内容,甚至自动驾驶汽车,AI都在大显身手。特别是在研发领域,…

软考笔记--软件可靠性设计

保障软件可靠性最有效、最经济、最重要的手段是在软件设计阶段采取措施进行可靠性控制。为了从根本上提高软件的可靠性,降低软件后期修改的成本和难度,人们提出了可靠性设计的概念。可靠性设计其实就是在常规软件设计中,应用各种方法和技术&a…

蓝桥杯倒计时 36天-DFS练习

文章目录 飞机降落仙境诅咒小怂爱水洼串变换 飞机降落 思路&#xff1a;贪心暴搜。 #include<bits/stdc.h>using namespace std; const int N 10; int t,n; //这题 N 比较小&#xff0c;可以用暴力搜搜复杂度是 TN*N! struct plane{int t,d,l; }p[N]; bool vis[N];//用…

基于springboot实现大学外卖管理系统项目【项目源码+论文说明】

基于springboot实现大学外卖管理系统演示 摘要 如今&#xff0c;信息化不断的高速发展&#xff0c;社会也跟着不断进步&#xff0c;现今的社会&#xff0c;各种工作都离不开信息化技术&#xff0c;更离不开电脑的管理。信息化技术也越来越渗透到各小型的企业和公司中&#xff…

C/C++蓝桥杯之REPEAT程序(较难)

问题描述&#xff1a; 附件prog.txt中是一个用某种语言编写的程序。 其中REPEAT k 表示一个次数为k的循环。循环控制的范围通过缩进表达&#xff0c;从次行开始连续的缩进比该行多的&#xff08;前面空白更长的&#xff09;为循环包含的内容。 例如&#xff1a; REPEAT 2; …

Java8 CompletableFuture异步编程-进阶篇

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前言 我们在前面文章讲解了CompletableFuture这个异步编程类的基本用法&#xff0c;…

Web Worker:JavaScript的后台任务解决方案

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Linux练习

作业要求&#xff1a; 自己安装linux环境&#xff0c;在每一个题目下贴执行命令和结果的截图 一、文件创建 1. 创建一个文件夹命名为mydir 2. 进入文件夹&#xff0c;创建一个文件&#xff0c;命名为myfile 3. 查看mydir文件夹下有哪些文件 答案获取&#xff1a; https:/…

SpringBoot项目没有启动按键

问题一&#xff1a; pom文件正常&#xff0c;但是springboot包报红&#xff0c;同时Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found报红 解决办法&#xff1a; 无法识别使用哪个版本的 spring-boot-maven-plugin 包 <build><plugins>&…

javase day01笔记

第一天课堂笔记 Java第三代高级语言中的面向对象的语言 b/s 浏览器/服务器c/s 客户端/服务端 1991年詹姆斯高斯林在sun公司开发的Java 常用的dos命令 磁盘操作系统&#xff1a;dos win &#xff0b; r -》 cmd dos命令 切换盘符&#xff1a;直接输入对应盘符目录操作&#x…

【排序算法】四个排序算法理论基础+Python代码:冒泡、插入、选择、快速排序

排序算法 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常见的内部排序算法有&#xff1a;插入排序、希尔排序…

【C++进阶】哈希的应用 --- 布隆过滤器

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会

写在开头 面试官&#xff1a;小伙子请聊一聊Java中的精灵线程&#xff1f; 我&#xff1a;什么&#xff1f;精灵线程&#xff1f;啥时候精灵线程&#xff1f; 面试官&#xff1a;精灵线程没听过&#xff1f;那守护线程呢&#xff1f; 我&#xff1a;守护线程知道&#xff0c;就…

计算机软件文档编制规范GB_T 8567-2006

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 计算机软件文档编制规范概述 计算机软件文档编制规范&#xff08;Specification for computer software documentation&#xff09; 由TC28&#xff08;全国信息技术标准化技…