日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

分布领域驱动设计(DDD):领域接口化设计式缓存的选择

發(fā)布時(shí)間:2025/3/16 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布领域驱动设计(DDD):领域接口化设计式缓存的选择 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


-? ? ?前言? ? -

把服務(wù)對(duì)象(service)和資源庫(kù)對(duì)象(repository)設(shè)計(jì)成接口是最常見的。但是這對(duì)接口化的認(rèn)識(shí)還遠(yuǎn)遠(yuǎn)不夠,我們需要更深入地去分析接口化設(shè)計(jì)和更全面地應(yīng)用接口化編程。所以我們要討論的是全面接口化,尤其是對(duì)領(lǐng)域模型接口化的認(rèn)識(shí)。


-? ? ?領(lǐng)域接口化? ? -

通常的情況下我們會(huì)把領(lǐng)域模型設(shè)計(jì)成類(class),但是你有沒有想過把領(lǐng)域模型設(shè)計(jì)成接口(interface)?比如:


public interface User {// ... }public class UserImpl implements User {// ... }


這樣的設(shè)計(jì)似乎沒有任何價(jià)值,那么繼續(xù)深入地看看。比如:

這時(shí)候看起來有點(diǎn)東西,因?yàn)槲覀優(yōu)榱诉m配不同的數(shù)據(jù)源,提供了不同的實(shí)現(xiàn)類。

最開始要把領(lǐng)域?qū)ο笤O(shè)計(jì)成接口,確實(shí)是為了在不同的 ORM 框架之間實(shí)現(xiàn)無(wú)縫切換。因?yàn)?JPA 對(duì)面向?qū)ο蟮闹С肿詈?#xff0c;而 Mybatis 因?yàn)楹?jiǎn)單在大環(huán)境下比較流行。在解決這個(gè)問題時(shí),通常使用層內(nèi)包裹或者叫對(duì)象轉(zhuǎn)換的方式來解決。具體來說是在持久層使用持久化對(duì)象(PO)與領(lǐng)域?qū)ο?#xff08;DO)的之間進(jìn)行轉(zhuǎn)換。例如:

public class JpaUserRepository implements UserRepository {// ...@Overridepublic Optional<User> findById(String id) {UserPO userPO = this.entityManager.find(UserPO.class, id);return Optional.ofNullable(userPO).map(UserPO::toUser);}@Overridepublic User save(User user) {UserPO userPO = this.entityManager.find(UserPO.class, user.getId());userPO.setNickname(user.getNickname());// ...return this.entityManager.merge(userPO).toUser();} }

其中 UserPO 對(duì)象基本上是對(duì)數(shù)據(jù)庫(kù)表的映射。

將 User 設(shè)計(jì)成接口后,這個(gè)交換的問題就比較簡(jiǎn)單地解決了,如下:

public class JpaUserRepository implements UserRepository { // ...@Overridepublic User create(String id) {return new JpaUser(id);}@Overridepublic Optional<User> findById(String id) {JpaUser user = this.entityManager.find(JpaUser.class, id);return Optional.ofNullable(user);}@Overridepublic User save(User user) {JpaUser target = JpaUser.of(user);return this.entityManager.merge(target);}// ... }補(bǔ)充 JpaUser.of()?方法的實(shí)現(xiàn):public class JpaUser extends UserSupport {// ...public static JpaUser of(User user) {if (user instanceof JpaUser) {return (JpaUser) user;}var target = new JpaUser();BeanUtils.copyProperties(user, target);// ...return target;} }

對(duì)于使用 JPA 或者 Elasticsearch 等等各種不同的數(shù)據(jù)源,Spring data 都為此做了全面的支持。但由于 User 是接口,Spring data 提供的 Repository 接口泛型只支持具體類型,比如:

public interface ElasticsearchUserRepositoryextends ElasticsearchRepository<ElasticsearchUser, String> {// extends ElasticsearchRepository<User, String> // Not supported }

為了解決這個(gè)問題,我們需要使用委托的方式,如下:

public class DelegatingElasticsearchUserRepository implements UserRepository {private final ElasticsearchUserRepository elasticsearchUserRepository;public DelegatingElasticsearchUserRepository(ElasticsearchUserRepository elasticsearchUserRepository) {this.elasticsearchUserRepository = elasticsearchUserRepository;}@Overridepublic User create(String id) {return new ElasticsearchUser(id);}@Overridepublic Optional<User> findById(String id) {return CastUtils.cast(this.elasticsearchUserRepository.findById(id));}@Overridepublic User save(User user) {return this.elasticsearchUserRepository.save(ElasticsearchUser.of(user));}// ... }


-? ? ?關(guān)聯(lián)接口化? ? -

接口之間的關(guān)聯(lián)關(guān)系依然需要具體到子類的關(guān)聯(lián)關(guān)系上來討論。

對(duì)于需要持久化的實(shí)體來說,我們不可能直接在成員屬性上使用接口類型,因?yàn)槌志没蚣軣o(wú)法通過接口來判定具體實(shí)現(xiàn)類。如下:

@Getter @Setter @NoArgsConstructor @Entity @Table(name = "mf_order") public class JpaOrder implements Order {// ...// OrderItem 是一個(gè)接口類型,不能持久化。private List<OrderItem> items = new ArrayList<>();// ... }

對(duì)于泛化關(guān)聯(lián)關(guān)系問題,我們可以使用 JPA 注解提供的 targetEntity 屬性來解決:

// ... public class JpaOrder implements Order {// ...// 通過指定具體的 targetEntity 類型,來解決泛化與特化的問題。@OneToMany(targetEntity = JpaOrderItem.class)private List<OrderItem> items = new ArrayList<>();// ... }
  • 支持 targetEntity 屬性的注解包括:@OneToMany、@OneToOne、@ManyToOne、@ManyToMany。

對(duì)于不支持類似 targetEntity 屬性的框架或者其它持久化技術(shù),我們可以使用封裝來解決。如下:

@Getter @Setter @NoArgsConstructor @Document(indexName = "user") public class ElasticsearchOrder implements Order {// ...// 使用具體特化類型進(jìn)行解決。private List<ElasticsearchOrderItem> items = new ArrayList<>();@Overridepublic void setItems(List<OrderItem> items) {this.items = Objects.requireNonNullElseGet(items, (Supplier<List<OrderItem>>) ArrayList::new).stream().map(ElasticsearchOrderItem::of).collect(Collectors.toList());}// ... }

如果使用的是 Mybatis 作為持久化框架,依然可以在 OrderMapper.xml 中進(jìn)行配置來解決:

<resultMap id="Order" type="org.mallfoundry.order.repository.mybatis.MybatisOrder"><!-- ... --><collection property="items" ofType="org.mallfoundry.order.repository.mybatis.MybatisOrderItem"><!-- ... --></collection><!-- ... --> </resultMap>

在解決掉不同數(shù)據(jù)源無(wú)縫切換和關(guān)聯(lián)關(guān)系特化的問題后,在創(chuàng)建 User 對(duì)象上就和以往使用 new 的方式有所不同了,如下:

@Test public void testCreateUser() {User user = this.userService.createUser(null); // new User()user.setNickname("Nickname");user.setGender(Gender.MALE);this.userService.addUser(user); }

再過去創(chuàng)建對(duì)象都是使用 new 關(guān)鍵字,然而現(xiàn)在要使用 UserService 提供的 createUser(String id)?來創(chuàng)建。

這種思維的轉(zhuǎn)變可能讓你初次不太很適應(yīng),但在考慮另一個(gè)問題。


-? ? ?系統(tǒng)接口化? ? -

對(duì)于一個(gè)產(chǎn)品我們要考慮的不只是產(chǎn)品本身能解決的業(yè)務(wù)需求,還需要在部署上有所追求。如果項(xiàng)目初期的并發(fā)量很小,客戶可能采用單進(jìn)程的方式部署,慢慢地單進(jìn)程扛不住了會(huì)升級(jí)到集群的方式,最終還要升級(jí)到微服務(wù)的方式。如何在單進(jìn)程、集群和微服務(wù)之間進(jìn)行無(wú)縫切換呢?

再過去單機(jī)和集群項(xiàng)目與微服務(wù)項(xiàng)目是不能兼容的,因?yàn)轭I(lǐng)域模型都是類(class)而不是接口(interface)。具體來說:服務(wù)提供者(provider)的 User 對(duì)象與服務(wù)消費(fèi)者(Consumer)的 User 對(duì)象是不兼容,不兼容將導(dǎo)致在單機(jī)項(xiàng)目中使用的是服務(wù)提供方的內(nèi)部 User 對(duì)象,而一旦遷移到微服務(wù)項(xiàng)目后,需要大量的修改工作。要把以前調(diào)用方使用內(nèi)部 User 對(duì)象替換為服務(wù)消費(fèi)者提供的 User 對(duì)象。這樣的工作也是不可以逆的,一旦遷移成功就不能降級(jí)到單機(jī)環(huán)境了。

再過去我們確實(shí)把服務(wù)(service)設(shè)計(jì)成了接口,這種接口的設(shè)計(jì)對(duì)于內(nèi)部的開發(fā)看似會(huì)有幫助,但是從實(shí)戰(zhàn)的經(jīng)驗(yàn)來看卻不像大家想象的那樣可以為 Service 提供不同的實(shí)現(xiàn)。因?yàn)楝F(xiàn)在都是迭代開發(fā),都是一個(gè)版本一個(gè)版本的去不斷完善應(yīng)用服務(wù)代碼,而不是替換應(yīng)用服務(wù)代碼,所以在 IDDD 中把應(yīng)用服務(wù)(Application Service)類型由接口(Interface)改為了類(Class)。

如果我們把領(lǐng)域?qū)ο笤O(shè)計(jì)成接口類型,并與服務(wù)接口以及其它接口一起組織在一個(gè)新的模塊內(nèi),形成一個(gè)新的接口(API)模塊。然后為各種不同地端口提供適配此端口的實(shí)現(xiàn),這樣的設(shè)計(jì)是不是可以解決在運(yùn)行環(huán)境中無(wú)縫切換的問題,如下:

這樣的設(shè)計(jì)使得調(diào)用者只需要使用 User 接口(user-api)開發(fā)業(yè)務(wù),并且在單進(jìn)程(Standalone)環(huán)境中只需要依賴 user 模塊,在微服務(wù)環(huán)境中只需要依賴 user-openfeign-client 模塊,在外部環(huán)境中只需要依賴 user-rest-client 模塊。調(diào)用者通過依賴不同地實(shí)現(xiàn)模塊來解決不同環(huán)境的無(wú)縫切換,并且調(diào)用者使用的代碼是不需要改變的。


-? ? ?開源電商? ? -

Mallfoundry 是一個(gè)完全開源的使用 Spring Boot 開發(fā)的多商戶電商平臺(tái)。它可以嵌入到已有的 Java 程序中,或者作為服務(wù)器、集群、云中的服務(wù)運(yùn)行。

  • 領(lǐng)域模型采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)、接口化以及面向?qū)ο笤O(shè)計(jì)。

項(xiàng)目地址:

https://gitee.com/mallfoundry/mall


-? ? ?總結(jié)? ? -

領(lǐng)域?qū)ο蠼涌诨沟梦覀冊(cè)趦?nèi)部實(shí)現(xiàn)了一套統(tǒng)一的接口,并將領(lǐng)域?qū)ο蠼涌诨瘮U(kuò)展到系統(tǒng)級(jí)別時(shí),我們又在系統(tǒng)層次上設(shè)計(jì)出一套統(tǒng)一地全局接口來開發(fā)業(yè)務(wù)和應(yīng)對(duì)未來變化的環(huán)境。這樣的設(shè)計(jì)雖然非常好,但對(duì)軟件設(shè)計(jì)人員、軟件架構(gòu)師以及開發(fā)人員的專業(yè)性也有了一定的要求,但是它所帶來的好處是可見的。

作者:不夠具體

來源:https://juejin.cn/post/6894109393173315597

推薦書籍

? ? · END ·

微信淘寶等平臺(tái)要互通!?騰訊阿里字節(jié)回應(yīng)

2021-09-14

一文詳解 API 設(shè)計(jì)最佳實(shí)踐

2021-09-12

12 種經(jīng)典億級(jí)流量架構(gòu)之資源隔離思想與方法論

2021-09-09

拼夕夕訂單超時(shí)未支付自動(dòng)關(guān)閉實(shí)現(xiàn)方案!

2021-09-08

在騰訊,我們?nèi)绾巫?Code Review

2021-09-24

紫色飛豬:基于K8s的集群穩(wěn)定架構(gòu)

2021-09-23

2W 字詳解設(shè)計(jì)模式!

2021-09-22

巨人大哥聊聊電商微服務(wù)體系中分層設(shè)計(jì)和領(lǐng)域的劃分

2021-09-20

億級(jí)流量架構(gòu)怎么做資源隔離?口琴這篇寫得太好了!

2021-09-17

螞蟻集團(tuán)于雨:萬(wàn)級(jí)規(guī)模 K8S 集群 Etcd 高可用建設(shè)之路

2021-09-16

干貨丨千萬(wàn)流量大型分布式系統(tǒng)架構(gòu)設(shè)計(jì)實(shí)戰(zhàn)

2021-09-15

京東面試官:你是怎么理解 MySQL 的優(yōu)化原理的?

2021-09-26

在騰訊,我們?nèi)绾巫?Code Review

2021-09-24

紫色飛豬:基于K8s的集群穩(wěn)定架構(gòu)

2021-09-23

2W 字詳解設(shè)計(jì)模式!

2021-09-22

巨人大哥聊聊電商微服務(wù)體系中分層設(shè)計(jì)和領(lǐng)域的劃分

2021-09-20

億級(jí)流量架構(gòu)怎么做資源隔離?口琴這篇寫得太好了!

2021-09-17

螞蟻集團(tuán)于雨:萬(wàn)級(jí)規(guī)模 K8S 集群 Etcd 高可用建設(shè)之路

2021-09-16

干貨丨千萬(wàn)流量大型分布式系統(tǒng)架構(gòu)設(shè)計(jì)實(shí)戰(zhàn)

2021-09-15

微信淘寶等平臺(tái)要互通!?騰訊阿里字節(jié)回應(yīng)

2021-09-14

一文詳解 API 設(shè)計(jì)最佳實(shí)踐

2021-09-12

12 種經(jīng)典億級(jí)流量架構(gòu)之資源隔離思想與方法論

2021-09-09

美團(tuán)技術(shù):到店結(jié)算平臺(tái)實(shí)踐(膠片)

2021-09-06

Serverless實(shí)戰(zhàn)之路

2021-09-03

柴華:DDD在哈啰交易中臺(tái)的實(shí)踐

2021-09-02

總結(jié)

以上是生活随笔為你收集整理的分布领域驱动设计(DDD):领域接口化设计式缓存的选择的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。