nextjs上的DDD架构

2024-03-10 01:04
文章标签 架构 nextjs ddd

本文主要是介绍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视角都很合理的架构。

这篇关于nextjs上的DDD架构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/792513

相关文章

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

响应式架构

介绍 响应式架构(Reactive Architecture)是一种面向服务和事件的系统设计方法,旨在提高系统的可扩展性、弹性和容错能力。它适用于构建分布式系统,特别是在云环境和微服务架构中。响应式架构的核心理念是通过事件驱动和数据流来实现各个组件之间的解耦,从而提高整个系统的响应能力和可靠性。 响应式架构的主要特点包括: 响应性:系统能够快速响应外部事件和内部变化,确保在各种负载和故障情

大型网站架构演化(六)——使用反向代理和CDN加速网站响应

随着网站业务不断发展,用户规模越来越大,由于中国复杂的网络环境,不同地区的用户访问网站时,速度差别也极大。有研究表明,网站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问速度。      主要手段:使用CDN和反向代理。如图。     使用CDN和反向代理的目的都是尽早返回数据给用户,一方面加快用户访问速

大型网站架构演化(五)——数据库读写分离

网站在使用缓存后,使绝大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作(缓存访问不命中、缓存过期)和全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过大而成为网站的瓶颈。      目前豆粉的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器上。网站利用数据库的这一功能,

大型网站架构演化(四)——使用应用服务器集群改善网站的并发能力

使用集群是网站解决高并发、海量数据问题的常用手段。当一台服务器的处理能力、存储空间不足时,不要企图去更换更强大的服务器,对大型服务器而言,不管多么强大的服务器,都满足不了网站持续增长的业务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器的访问及存储压力。 对网站架构而言,只要能通过增加一台服务器的方式改善负载压力,就可以以同样的方式持续增加服务器不断改善系统性能,从而实现系统

大型网站架构演化(二)——应用服务和数据服务分离

随着网站业务的发展,一台服务器逐渐不能满足需求:越来越多的用户访问导致性能越来越差,越来越多的数据导致存储空间不足。这时就需要将应用和数据分离。应用和数据分离后整个网站使用三台服务器:应用服务器、文件服务器和数据库服务器,如图。              这三台服务器对硬件资源的要求各不相同: 应用服务器需要处理大量的业务逻辑,因此需要更快更强大的CPU;

大型网站架构演化(一)——初始阶段的网站架构

大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量的数据,任何简单的业务一旦需要处理数以P计的数据和面对数以亿计的用户,问题就会变得很棘手。大型网站架构主要是解决这类问题。         大型网站都是从小型网站发展而来,网站架构也是一样,是从小型网站架构逐步演化而来。小型网站最开始时没有太多人访问,只需要一台服务器就绰绰有余,这时的网站架构如图所示。

大型网站架构演化(总)

如果把上世纪90年代初CERN正式发布WEB标准和第一个WEB服务的出现当做互联网站的开始,那么互联网站的发展只经历了短短20多年的时间。在20多年的时间里,互联网的世界发生了巨大变化,今天,全球有近一半的人口使用互联网,人们的生活因为互联网而产生了巨大变化。从信息检索到即时通信,从电子购物到文化娱乐,互联网渗透到生活的每个角落,而且这种趋势还在加速。因为互联网,我们的世界正变得越来越小

Java中的大数据处理与分析架构

Java中的大数据处理与分析架构 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来讨论Java中的大数据处理与分析架构。随着大数据时代的到来,海量数据的存储、处理和分析变得至关重要。Java作为一门广泛使用的编程语言,在大数据领域有着广泛的应用。本文将介绍Java在大数据处理和分析中的关键技术和架构设计。 大数据处理与

hbase架构

本篇文章旨在针对初学者以我本人现阶段所掌握的知识就HBase的架构图中各模块作一个概念科普。不对文章内容的“绝对、完全正确性”负责。  1、开胃小菜   关于HBase的架构图,直接抓取网络上图片来分析就好了。它大概长成下面的样子: 图1 HBase架构图   从上图中可以很直观地看到整个HBase都是基于HDFS之上的。这个HDFS呢,它的全称是Hadoop distribut