国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

這篇具有很好參考價(jià)值的文章主要介紹了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Driven Design,簡(jiǎn)稱:DDD)設(shè)計(jì)思想和方法論早在2005年時(shí)候就被提出來(lái),但是一直沒有被重視和推薦使用,直到2015年之后微服務(wù)流行之后,再次被人重視和推薦使用。

下面我來(lái)介紹一下DDD設(shè)計(jì)思想和方法論,同時(shí)結(jié)合我們?cè)趯?shí)際項(xiàng)目中應(yīng)用總結(jié)和思考。

目錄

1、為什么要用DDD

2、DDD架構(gòu)設(shè)計(jì)

3、領(lǐng)域建模方法

4、代碼實(shí)踐

5、問題總結(jié)

?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-482315.html

1、為什么要用DDD


面向過(guò)程
? ? ? 很多時(shí)候,我們都是操著面向?qū)ο蟮恼Z(yǔ)言干著面向過(guò)程的勾當(dāng)。大部分的系統(tǒng)都是從單一業(yè)務(wù)開始的。但是隨著支持的業(yè)務(wù)越來(lái)越多,代碼里面開始出現(xiàn)大量的if-else邏輯,這個(gè)時(shí)候代碼開始有壞味道,沒聞到的同學(xué)就這么繼續(xù)往上堆,聞到的同學(xué)會(huì)重構(gòu)一下,但因?yàn)橄到y(tǒng)沒有統(tǒng)一的可擴(kuò)展架構(gòu),重構(gòu)的技法也各不相同,這種代碼的不一致性也是一種理解上的復(fù)雜度。

分層不合理
? ? ? 分層太多和沒有分層都會(huì)導(dǎo)致系統(tǒng)復(fù)雜度的上升。

隨心所欲
? ? ? 隨心所欲是因?yàn)槿鄙僖?guī)范和約束。

面向?qū)ο?/strong>
? ? ?深入的理解面向?qū)ο笤O(shè)計(jì)原則、模式、方法論有很多,同時(shí)要具備非常好的業(yè)務(wù)理解力和抽象能力。
? ? ?SOLID是單一職責(zé)原則(SRP),開閉原則(OCP),里氏替換原則(LSP),接口隔離原則(ISP)和依賴倒置原則(DIP)的縮寫,原則是要比模式更基礎(chǔ)更重要的指導(dǎo)準(zhǔn)則,深入理解面向?qū)ο笤O(shè)計(jì)原則極大的提升我們的面向?qū)ο笤O(shè)計(jì)的能力和代碼質(zhì)量。

分層設(shè)計(jì)
? ? ? 分層最大的好處就是分離關(guān)注點(diǎn),讓每一層只解決該層關(guān)注的問題,從而將復(fù)雜的問題簡(jiǎn)化,起到分而治之的作用。

領(lǐng)域建模
? ? ? 領(lǐng)域模型可以準(zhǔn)確地反映業(yè)務(wù)語(yǔ)言,使業(yè)務(wù)語(yǔ)義顯性化,而傳統(tǒng)的J2EE或Spring+Hibernate/Mybatis等事務(wù)性編程模型只關(guān)心數(shù)據(jù),這些數(shù)據(jù)對(duì)象除了簡(jiǎn)單setter/getter方法外,沒有任何業(yè)務(wù)方法,被稱為成貧血模式。

規(guī)范設(shè)計(jì)
? ? ? ? 各歸其位、命名約定、通用業(yè)務(wù)狀態(tài)碼約定等。

DDD概念定義

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):一種軟件開發(fā)方法,是一種軟件核心復(fù)雜性應(yīng)對(duì)之道,它可以幫助我們?cè)O(shè)計(jì)高質(zhì)量的軟件模型。

DDD目的:

  1. DDD是為了解決復(fù)雜業(yè)務(wù)邏輯的問題
  2. DDD的核心問題不是技術(shù)問題,而是關(guān)于討論、聆聽、理解、發(fā)現(xiàn)業(yè)務(wù)價(jià)值的問題
  3. DDD是技術(shù)人員擁有產(chǎn)品思維的一個(gè)體現(xiàn)
  4. DDD的學(xué)習(xí)曲線很陡主要是因?yàn)樗且环N方法論,每個(gè)人對(duì)它的理解存在差異

?

領(lǐng)域模型與事務(wù)腳本對(duì)比

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

富血模型:就是屬性和方法都封裝在Domain Object里,所有業(yè)務(wù)都直接操作Domain Object。 service層只是完成一些簡(jiǎn)單的事務(wù)之類的,甚至可以不用service層。也就是直接從action->entity。

貧血模型:就是一個(gè)對(duì)象里只有屬性,要實(shí)現(xiàn)業(yè)務(wù),要依靠service層來(lái)實(shí)現(xiàn)相關(guān)方法,service層的實(shí)現(xiàn)是面向過(guò)程的,大量傳統(tǒng)的分層應(yīng)用action->service->dao->entity的方式就是這種貧血的模式實(shí)現(xiàn)。

舉個(gè)例子,銀行轉(zhuǎn)賬實(shí)現(xiàn)類

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

各位看官看到上面的代碼是不是有一種似曾相似的感覺?咋一看也沒有啥問題,能正常運(yùn)行,能快速交付業(yè)務(wù)。如果僅是為了應(yīng)付需求任務(wù)交差,當(dāng)然沒有什么啥問題了。

從設(shè)計(jì)角度來(lái)看確實(shí)存在一些問題,代碼糊在一起,沒有分層設(shè)計(jì),偽面向?qū)ο蟮姆椒ā?/span>

我們碼農(nóng)總得要有自己一點(diǎn)的追求,為偉大代碼事業(yè)創(chuàng)造一點(diǎn)藝術(shù)貢獻(xiàn)!

  • 如果業(yè)務(wù)需求快速變化怎么辦,需求越來(lái)越復(fù)雜怎么辦?
  • 如果團(tuán)隊(duì)里面有多人協(xié)作,代碼需要經(jīng)過(guò)多人反復(fù)修改接手傳承,你敢保證后面接手的人不會(huì)罵你嗎?
  • 難道設(shè)計(jì)上有沒有可以改進(jìn)的空間?

答案是正面的!

客觀來(lái)說(shuō)上面的這段代碼不算復(fù)雜,也算寫得中規(guī)中舉。下面我來(lái)讓貼一段曾經(jīng)在我們實(shí)際生產(chǎn)系統(tǒng)跑了將近一年的神代碼,你才知道什么叫復(fù)雜和神奇!16層if..else+for循環(huán)!就問你怕了沒有?

》》》請(qǐng)點(diǎn)開看下面神碼片段????

請(qǐng)看神碼--地獄18層!
 if (contentOld != null && contentNew != null) {
            map.put("ModelId", contentNew.getModelId());//ModelId
            map.put("name", contentNew.getName());//Name
            map.put("Description", contentNew.getDescription());//Description
            map.put("fujianGroup", attachments);//附件組信息
            for (int i = 0; i < contentOld.getGroups().size(); i++) {//數(shù)據(jù)庫(kù)保存 組
                MscsApiShowerRoomModelGroups groupsOld = contentOld.getGroups().get(i);
                if ("納米易結(jié)".equals(groupsOld.getGroupName())) {//新增的組信息
                    map.put("nanometerGroup", groupsOld);//納米易結(jié)
                } else if ("石基先發(fā)".equals(groupsOld.getGroupName())) {
                    map.put("stoneGroupFirst", groupsOld);//石基先發(fā)
                } else if ("客戶其它內(nèi)容".equals(groupsOld.getGroupName())) {
                    map.put("otherGroup", groupsOld);//客戶其它內(nèi)容
                } else {//原來(lái)的組 通過(guò)組ID 來(lái)判定
                    if ("1".equals(groupsOld.getGroupId())) {//產(chǎn)品規(guī)格組
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                groups.setOtherValue(groupsOld.getOtherValue());//設(shè)置單選按鈕
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                            for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                                for (MscsApiShowerRoomModelProperties properties : options.getProperties()) {
                                                                    for (MscsApiShowerRoomModelProperties properties2 : options2.getProperties()) {
                                                                        if (properties.getPropertyId() != null && properties.getPropertyId().equals(properties2.getPropertyId())) {
                                                                            properties2.setDefaults(properties.getDefaults());
                                                                        }
                                                                    }
                                                                }
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("productGroup", groups);//產(chǎn)品規(guī)格組
                            }
                        }
//						map.put("productGroup",null);//產(chǎn)品規(guī)格組
                    } else if ("2".equals(groupsOld.getGroupId())) {//開門方向與產(chǎn)品方向
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                            for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("doorGroup", groups);//開門方向與產(chǎn)品方向
                            }
                        }

                    } else if ("3".equals(groupsOld.getGroupId())) {//產(chǎn)品顏色
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                            for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("productColorGroup", groups);//產(chǎn)品顏色
                            }
                        }

                    } else if ("14".equals(groupsOld.getGroupId())) {//玻璃工藝
                        String name = "";
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                groups.setOtherValue(groupsOld.getOtherValue());
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                        for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                            if (categories.getName() != null && categories.getName().equals(categories2.getName())) {
                                                name = categories.getName();
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                //設(shè)置 默認(rèn)數(shù)據(jù)的位置 選中的數(shù)組 為第一個(gè) 默認(rèn)顯現(xiàn)
                                if (groups.getGategories() != null && !"".equals(name)) {
                                    List<MscsApiShowerRoomModelCategories> arry = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry1 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry2 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    for (MscsApiShowerRoomModelCategories categories : groups.getGategories()) {
                                        if (name.equals(categories.getName())) {
                                            arry1.add(categories);
                                        } else {
                                            arry2.add(categories);
                                        }

                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry1) {
                                        arry.add(a);
                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry2) {
                                        arry.add(a);
                                    }
                                    groups.setGategories(arry);
                                }
                                map.put("glassGroup", groups);//玻璃工藝
                            }
                        }
//						map.put("glassGroup", null);//玻璃工藝
                    } else if ("4".equals(groupsOld.getGroupId())) {//玻璃貼膜
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                groups.setOtherValue(groupsOld.getOtherValue());
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                        for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                            if (categories.getName() != null && categories.getName().equals(categories2.getName())) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("filmGroup", groups);//玻璃貼膜
//								break ;
                            }
                        }
                    } else if ("5".equals(groupsOld.getGroupId())) {//玻璃厚度
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                        for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                            if (categories.getName() != null && categories.getName().equals(categories2.getName())) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("thicknessGroup", groups);//玻璃厚度
                            }
                        }
                    } else if ("6".equals(groupsOld.getGroupId())) {//拉手款式
                        String name = "";
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                        for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                            if (categories.getName() != null && categories.getName().equals(categories2.getName())) {
                                                name = categories.getName();
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                //設(shè)置 默認(rèn)數(shù)據(jù)的位置 選中的數(shù)組 為第一個(gè) 默認(rèn)顯現(xiàn)
                                if (groups.getGategories() != null && !"".equals(name)) {
                                    List<MscsApiShowerRoomModelCategories> arry = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry1 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry2 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    for (MscsApiShowerRoomModelCategories categories : groups.getGategories()) {
                                        if (name.equals(categories.getName())) {
                                            arry1.add(categories);
                                        } else {
                                            arry2.add(categories);
                                        }

                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry1) {
                                        arry.add(a);
                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry2) {
                                        arry.add(a);
                                    }
                                    groups.setGategories(arry);
                                }
                                map.put("handleGroup", groups);//拉手款式
                            }
                        }
//						map.put("handleGroup", null);//拉手款式
                    } else if ("7".equals(groupsOld.getGroupId())) {//石基
                        String name = "";
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                        for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                            if (categories.getName() != null && categories.getName().equals(categories2.getName())) {
                                                name = categories.getName();
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                //設(shè)置 默認(rèn)數(shù)據(jù)的位置 選中的數(shù)組 為第一個(gè) 默認(rèn)顯現(xiàn)
                                if (groups.getGategories() != null && !"".equals(name)) {
                                    List<MscsApiShowerRoomModelCategories> arry = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry1 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    List<MscsApiShowerRoomModelCategories> arry2 = new ArrayList<MscsApiShowerRoomModelCategories>();
                                    for (MscsApiShowerRoomModelCategories categories : groups.getGategories()) {
                                        if (name.equals(categories.getName())) {
                                            arry1.add(categories);
                                        } else {
                                            arry2.add(categories);
                                        }

                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry1) {
                                        arry.add(a);
                                    }
                                    for (MscsApiShowerRoomModelCategories a : arry2) {
                                        arry.add(a);
                                    }
                                    groups.setGategories(arry);
                                }
                                map.put("stoneGroup", groups);//拉手款式
                            }
                        }
                    } else if ("8".equals(groupsOld.getGroupId())) {//層架
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                            for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                                for (MscsApiShowerRoomModelProperties properties : options.getProperties()) {
                                                                    for (MscsApiShowerRoomModelProperties properties2 : options2.getProperties()) {
                                                                        if (properties.getPropertyId() != null && properties.getPropertyId().equals(properties2.getPropertyId())) {
                                                                            properties2.setDefaults(properties.getDefaults());
                                                                        }
                                                                    }
                                                                }
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("shelfGroup", groups);//層架
                            }
                        }
//						map.put("shelfGroup", null);//層架
                    } else if ("9".equals(groupsOld.getGroupId())) {//木架包裝
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                            for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                    for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                        for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                            if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                            } else {
                                                                options2.setIsDefault(false);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                map.put("mujiaGroup", groups);//木架包裝
                            }
                        }
                    } else if ("32".equals(groupsOld.getGroupId())) {//玻璃寬度
                        for (MscsApiShowerRoomModelGroups groups : contentNew.getGroups()) {
                            if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                if (groupsOld.getGroupId().equals(groups.getGroupId())) {
                                    if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                        if (groupsOld.getGategories() != null && groups.getGategories() != null) {
                                            for (MscsApiShowerRoomModelCategories categories : groupsOld.getGategories()) {
                                                for (MscsApiShowerRoomModelCategories categories2 : groups.getGategories()) {
                                                    if (categories.getOptions() != null && categories2.getOptions() != null) {
                                                        for (MscsApiShowerRoomModelOptions options : categories.getOptions()) {
                                                            for (MscsApiShowerRoomModelOptions options2 : categories2.getOptions()) {
                                                                if (options.getOptionId() != null && options.getOptionId().equals(options2.getOptionId())) {
                                                                    options2.setIsDefault(true);//設(shè)置為默認(rèn)選項(xiàng)
                                                                    if (options.getProperties() != null) {
                                                                        for (MscsApiShowerRoomModelProperties properties : options.getProperties()) {
                                                                            for (MscsApiShowerRoomModelProperties properties2 : options2.getProperties()) {
                                                                                if (StringUtils.isNotEmpty(properties.getCode()) && properties.getCode().equals(properties2.getCode())) {
                                                                                    properties2.setDefaults(properties.getDefaults());
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                } else {
                                                                    options2.setIsDefault(false);
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    map.put("glassWidthGroup", groups);// 玻璃寬度
                                }
                            }
                        }
                    }
                }
            }
        }

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

各位看官你們看,看完是不是很想吐血!像上面這種代碼請(qǐng)問誰(shuí)敢去維護(hù)?誰(shuí)看誰(shuí)罵,誰(shuí)改誰(shuí)死!這種神代碼絕對(duì)是可以拿來(lái)當(dāng)教材用的~(在這里展示這段代碼主要為了說(shuō)明如果系統(tǒng)設(shè)計(jì)和分層不合理,將會(huì)帶來(lái)嚴(yán)重的后果,可以說(shuō)代碼就像狗屎一樣)

PS:說(shuō)明一下當(dāng)時(shí)寫這些代碼的作者因?yàn)橐恍┰螂x職了,我們當(dāng)年系統(tǒng)上線后將近一年多的時(shí)間里不敢去修改這神代碼,也沒有人敢改。

如果業(yè)務(wù)需求一直穩(wěn)定不改,那可以還勉強(qiáng)維持著,但是那有正常業(yè)務(wù)不改需求的?天底下應(yīng)該沒有!所以它始終像一顆大雷在我們的頭頂上滾動(dòng)著!

后來(lái)經(jīng)過(guò)兩次重大重構(gòu)設(shè)計(jì)之后,把它消滅了!篇幅原因這里的故事就不展開講了,有興趣的請(qǐng)看另一篇文章:《那些年那些神碼》。

回到主題上,銀行轉(zhuǎn)賬那一段代碼對(duì)比之下是不是小屋見大屋?下面我們基于銀行轉(zhuǎn)賬那一段代碼,展示一下怎么拆解和設(shè)計(jì),變成領(lǐng)域設(shè)計(jì)模型。

轉(zhuǎn)變成領(lǐng)域設(shè)計(jì)如下:

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

  • 抽出Account Domain類;
  • 抽出轉(zhuǎn)賬類即領(lǐng)域服務(wù);
  • 抽出透支策略接口與實(shí)現(xiàn)類

重新拆解轉(zhuǎn)換之后,設(shè)計(jì)領(lǐng)域?qū)ο螅a分層結(jié)構(gòu)清晰了很多,后續(xù)業(yè)務(wù)折騰變化容易維護(hù)和擴(kuò)展,從此世界也變得清新了,你說(shuō)香不香嗎?

?

2、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)設(shè)計(jì)

2.1、分層結(jié)構(gòu)轉(zhuǎn)變

先看分層結(jié)構(gòu)思路轉(zhuǎn)變,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)分層與傳統(tǒng)三層結(jié)構(gòu)有比較直觀的區(qū)別。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

User Interface為用戶接口層,也是經(jīng)常我們看到Controller層類似,主要負(fù)責(zé)系統(tǒng)入口協(xié)議等,該層不處理任何業(yè)務(wù)邏輯,只負(fù)責(zé)調(diào)用接入和應(yīng)用轉(zhuǎn)發(fā)。

Application是應(yīng)用層,和以往事務(wù)腳本的Service是截然不同的,該層中并不做詳細(xì)的業(yè)務(wù)邏輯的封裝,而是創(chuàng)建Domian并調(diào)用Domain中的相應(yīng)方法完成業(yè)務(wù)邏輯處理,并調(diào)用Infrastructure完成Domain的持久化操作,該層需要負(fù)責(zé)事務(wù)的控制,保證數(shù)據(jù)的一致性。

Domain層是核心領(lǐng)域?qū)?/strong>,核心的業(yè)務(wù)邏輯應(yīng)該以Domain為對(duì)象進(jìn)行分類封裝,Domain的劃分以業(yè)務(wù)為基準(zhǔn),Domain層在技術(shù)層面的建模通用技巧在下面會(huì)做詳細(xì)介紹,該層只能向下調(diào)用Infrastructure完成自身模型的初始化和持久化。

Infrastructure層類似于以往事務(wù)腳本的Dao層,以往的面向事務(wù)腳本的編程中,以數(shù)據(jù)表為主,所有的事務(wù)腳本直指目的就是完成表的CURD,而DDD中以模型為核心,Infrastructure層是為了重建已有的Domain,并在退出時(shí)持久化內(nèi)存中的Domain對(duì)象。Infrastructure層不僅包含對(duì)關(guān)系數(shù)據(jù)庫(kù)的處理,還包括對(duì)分布式緩存處理、對(duì)外系統(tǒng)的接入(integration)以及分布式消息隊(duì)列的push操作。

一些常用的關(guān)鍵術(shù)語(yǔ):

  • 實(shí)體 - Entity
  • 值對(duì)象 - Value Objects
  • 領(lǐng)域服務(wù) - Domain Services
  • 領(lǐng)域事件 - Domain Events
  • 模塊 - Modules
  • 聚合 - Aggregate
  • 資源庫(kù) - Repository

實(shí)體和值對(duì)象在代碼中皆表示為一個(gè)類(對(duì)象),從業(yè)務(wù)上來(lái)講也分別表示一個(gè)業(yè)務(wù)實(shí)體。但是兩者是有區(qū)別,實(shí)體是一個(gè)完整的具有生命周期的可以通過(guò)唯一標(biāo)識(shí)來(lái)識(shí)別東西的類(對(duì)象),而值對(duì)象則為了度量和描述領(lǐng)域中的一個(gè)屬性,將不同的相關(guān)屬性組合成一個(gè)概念整體的類(對(duì)象) 。?

例如,客戶可以認(rèn)為是一個(gè)實(shí)體,一個(gè)客戶就是具有生命周期的東西,具有唯一的標(biāo)識(shí)可以將A客戶和B客戶分開(唯一標(biāo)識(shí):身份證號(hào)碼),而這個(gè)客戶的地址(例如:廣州市/白云區(qū)/歐派軟件園)就應(yīng)該定義為一個(gè)值對(duì)象,當(dāng)我們定義好一個(gè)地址,它既可以是A客戶的地址也可以是B客戶的地址,當(dāng)我需要更新A客戶地址時(shí),可以直接銷毀A客戶關(guān)聯(lián)的地址對(duì)象然后重新創(chuàng)建一個(gè)地址對(duì)象關(guān)聯(lián)到A客戶上。

領(lǐng)域事件是由一個(gè)特定領(lǐng)域因?yàn)橐粋€(gè)用戶Command觸發(fā)的發(fā)生在過(guò)去的行為產(chǎn)生的事件,而這個(gè)事件是系統(tǒng)中其它部分感興趣的。

在現(xiàn)在的分布式環(huán)境下,業(yè)務(wù)系統(tǒng)與業(yè)務(wù)系統(tǒng)之間并不是割裂的,而消息絕對(duì)是系統(tǒng)之間耦合度最低,最健壯,最容易擴(kuò)展的一種通信機(jī)制。

領(lǐng)域服務(wù)和以往事務(wù)腳本的Service只能說(shuō)非常像,唯一不同的是,以往事務(wù)腳本的Service是自己全權(quán)負(fù)責(zé)業(yè)務(wù)邏輯,而領(lǐng)域服務(wù)不僅編寫業(yè)務(wù)邏輯,還會(huì)調(diào)用實(shí)體和值對(duì)象的方法來(lái)協(xié)調(diào)實(shí)體和值對(duì)象,從而避免實(shí)體和值對(duì)象的耦合。當(dāng)我們建模之后發(fā)現(xiàn)有些業(yè)務(wù)既不單單屬于A領(lǐng)域?qū)ο?,又不單單屬于B領(lǐng)域?qū)ο?,我們可以那么我們可以抽象出一個(gè)Service來(lái)完成此項(xiàng)業(yè)務(wù),那么這個(gè)類就是領(lǐng)域服務(wù)。

資源庫(kù)也叫數(shù)據(jù)倉(cāng)庫(kù),主要是完成領(lǐng)域?qū)ο蟮闹亟ㄒ约皩?duì)象的持久化操作。資源庫(kù)的設(shè)計(jì)主要是為了調(diào)用基礎(chǔ)設(shè)施層來(lái)完成表的CURD、緩存的操作以及外系統(tǒng)接口的調(diào)用。

?

2.2、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)

這里引用阿里團(tuán)隊(duì)開源的可樂(Cola)領(lǐng)域設(shè)計(jì)架構(gòu)示圖,如下圖所示:

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

DDD最核心思想是設(shè)計(jì)內(nèi)部六邊形結(jié)構(gòu)。

從內(nèi)往外看,最內(nèi)層也是最核心就是Domain層(包括:Domain model和Domain Service);

第二層是Application層(包括:Application Service);

第三層業(yè)務(wù)邏輯層(Business Logic也可以叫基礎(chǔ)設(shè)施層)主對(duì)外提供服務(wù)接口,對(duì)內(nèi)解決基礎(chǔ)服務(wù)包裝構(gòu)建。

?

2.3、分層調(diào)用示圖

DDD分層架構(gòu)設(shè)計(jì)之調(diào)用示圖

?

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

上圖已經(jīng)非常清楚展示了分層設(shè)計(jì)以及各層調(diào)用關(guān)系,一圖勝千言,大家認(rèn)真詳細(xì)看圖就可以理解了。

2.4、命令式讀寫分離

操作命令和對(duì)象抽象,通過(guò)命令與查詢分離設(shè)計(jì)方式實(shí)現(xiàn)服務(wù)接口調(diào)用。

CQRS(Command Query Separation,命令查詢分離) 基本思想在于,任何一個(gè)對(duì)象的方法可以分為兩大類:

  • 命令(Command):不返回任何結(jié)果(void),但會(huì)改變對(duì)象的狀態(tài)
  • 查詢(Query):返回結(jié)果,但是不會(huì)改變對(duì)象的狀態(tài),對(duì)系統(tǒng)沒有副作用

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

2.5、擴(kuò)展點(diǎn)

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)另外一個(gè)重要的地方是擴(kuò)展點(diǎn)。

可樂包在擴(kuò)展點(diǎn)功能設(shè)計(jì)中引入的概念:業(yè)務(wù)、用例、場(chǎng)景。

  • 業(yè)務(wù)(Business):就是一個(gè)自負(fù)盈虧的財(cái)務(wù)主體,比如tmall、淘寶和零售通就是三個(gè)不同的業(yè)務(wù)。
  • 用例(Use Case):描述了用戶和系統(tǒng)之間的互動(dòng),每個(gè)用例提供了一個(gè)或多個(gè)場(chǎng)景。比如,支付訂單就是一個(gè)典型的用例。
  • 場(chǎng)景(Scenario):場(chǎng)景也被稱為用例的實(shí)例,包括用例所有的可能情況(正常的和異常的)。比如對(duì)于“訂單支付”這個(gè)用例,就有“可以使用花唄”,“支付寶余額不足”,“銀行賬戶余額不足”等多個(gè)場(chǎng)景。

?

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

例如我們要實(shí)現(xiàn)右圖中所展示的擴(kuò)展:在tmall這個(gè)業(yè)務(wù)下——下單用例——88VIP場(chǎng)景——用戶身份校驗(yàn)進(jìn)行擴(kuò)展,我們只需要聲明一個(gè)如下的擴(kuò)展實(shí)現(xiàn)(Extension)就可以了。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

?

3、領(lǐng)域建模

領(lǐng)域架構(gòu)設(shè)計(jì)并不復(fù)雜,復(fù)雜的業(yè)務(wù)需求怎么轉(zhuǎn)化為領(lǐng)域模型,這也是最難的地方,需要業(yè)務(wù)梳理足夠清晰,同時(shí)需要有足夠抽象能力和領(lǐng)域劃分。

領(lǐng)域建模基于業(yè)務(wù)的建模的基礎(chǔ)上,需要花較重的時(shí)間比例在梳理業(yè)務(wù)和模型設(shè)計(jì)上面;而同時(shí)領(lǐng)域建沒有通用的統(tǒng)一結(jié)構(gòu)設(shè)計(jì),得看具體業(yè)務(wù)具體分析。下面舉個(gè)例子說(shuō)明一下領(lǐng)域建模方法。希望能夠各位得到一點(diǎn)啟發(fā)。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

領(lǐng)域模型不是簡(jiǎn)單POJO或數(shù)據(jù)實(shí)體對(duì)象,他還有行為和狀態(tài),主要體現(xiàn)在事件機(jī)制、值對(duì)象上面。

這里先不深入討論,先拋個(gè)影子,后面抽空補(bǔ)上更詳細(xì)的說(shuō)明。

?

?

4、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)現(xiàn)

?

4.1、分層設(shè)計(jì)

各層分工:

  • Application層主要負(fù)責(zé)獲取輸入,組裝context,做輸入校驗(yàn),發(fā)送消息給領(lǐng)域?qū)幼鰳I(yè)務(wù)處理,監(jiān)聽確認(rèn)消息;
  • Domain層主要是通過(guò)領(lǐng)域服務(wù)(Domain Service),領(lǐng)域?qū)ο螅―omain Object)的交互,對(duì)上層提供業(yè)務(wù)邏輯的處理,然后調(diào)用下層Repository做持久化處理;
  • Infrastructure層主要包含Repository,Config,Common和message,Repository負(fù)責(zé)數(shù)據(jù)的CRUD操作,數(shù)據(jù)來(lái)源可以是MySQL,NoSql,Search,甚至是HSF等;
  • Config負(fù)責(zé)應(yīng)用的配置;Common是一寫工具類;負(fù)責(zé)message通信的也應(yīng)該放在這一層。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

4.2、建立二方庫(kù)組件

二方庫(kù)組件:
api:存放的是應(yīng)用對(duì)外的接口。
dto.domainmodel:用來(lái)做數(shù)據(jù)傳輸?shù)妮p量級(jí)領(lǐng)域?qū)ο蟆?/span>
dto.domainevent: 用來(lái)做數(shù)據(jù)傳輸?shù)念I(lǐng)域事件。
Application里的組件:
service:接口實(shí)現(xiàn)的facade,沒有業(yè)務(wù)邏輯,可以包含對(duì)不同終端的adapter。
eventhandler:處理領(lǐng)域事件,包括本域的和外域的。
executor:用來(lái)處理(Command)和查詢(Query)。
interceptor:COLA提供的對(duì)所有請(qǐng)求的AOP處理機(jī)制。
Domain里的組件:
domain:領(lǐng)域?qū)嶓w,允許繼承domainmodel。
domainservice: 領(lǐng)域服務(wù),用來(lái)提供更粗粒度的領(lǐng)域能力。
gateway:對(duì)外依賴的網(wǎng)關(guān)接口,包括存儲(chǔ)、RPC、Search等。

?

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

大部分情況下,二方庫(kù)的確是用來(lái)定義服務(wù)接口和數(shù)據(jù)協(xié)議的。但是二方庫(kù)區(qū)別于JSON的地方是它不僅僅是協(xié)議,它還是一個(gè)Java對(duì)象,一個(gè)Jar包。既然是Java對(duì)象,就意味著我們就有可能讓DTO升級(jí)為一個(gè)Domain Model(有數(shù)據(jù),有行為,有繼承) 。
Domain Model用到的所有數(shù)據(jù)如果都是自恰的,那么這些計(jì)算是不需要借助外面的輔助,自己就能完成。比如CustomerCO擁有身份證號(hào)碼,那么判斷當(dāng)前這個(gè)CustomerCO的性別、年齡等信息時(shí)是依靠自己(身份證號(hào)碼)就能完成的。是一種共享內(nèi)核, CustomerCO把自己領(lǐng)域的知識(shí)(語(yǔ)言、數(shù)據(jù)和行為)通過(guò)二方庫(kù)暴露出去了,假如有100個(gè)應(yīng)用需要獲取性別或年齡時(shí)做判斷,客戶端就不需要自己實(shí)現(xiàn)了。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

4.3、上下文

應(yīng)用不同Bounded Context之間的協(xié)作,要充分利用好二方庫(kù)的橋梁作用。其協(xié)作方式如下圖所示:

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

4.4、主要組件依賴關(guān)系

依賴關(guān)系示例,如以Customer會(huì)員業(yè)務(wù)對(duì)象舉例如下圖所示

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

4.5、代碼實(shí)現(xiàn)

下面以我們系統(tǒng)中客戶中心會(huì)員體系設(shè)計(jì)為示例介紹一下怎么使用DDD方法實(shí)現(xiàn)代碼。

對(duì)外接口代碼示例

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

參數(shù)校驗(yàn)

?

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

API接口服務(wù)層示例

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

命令總線示例

?

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

全局異常捕獲示例代碼

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

?

4.6、舊項(xiàng)目神碼改造

對(duì)于舊項(xiàng)目代碼,大家都非常頭痛,舊系統(tǒng)經(jīng)常出現(xiàn)一些奇怪的問題,其實(shí)就是不穩(wěn)定引起的。舊系統(tǒng)代碼都是神碼具多,極難維護(hù),誰(shuí)都不愿意接手,后來(lái)經(jīng)過(guò)我們重大討論決策后決定對(duì)舊項(xiàng)目進(jìn)行重構(gòu)。

重構(gòu)說(shuō)起來(lái)簡(jiǎn)單,實(shí)施起來(lái)卻是非常的頭大,畢竟不是簡(jiǎn)單的系統(tǒng),同時(shí)也是公司里面業(yè)務(wù)最重要的業(yè)務(wù)系統(tǒng)(訂單+CRM集團(tuán)業(yè)績(jī)的入口保障),不容許出錯(cuò);而舊代碼庫(kù)非常龐大,規(guī)模接近百萬(wàn)行。代碼質(zhì)量不堪回首,都是地獄級(jí)別。

我們痛定思痛,決定對(duì)它動(dòng)用外科手術(shù),當(dāng)時(shí)頂著巨大壓力說(shuō)服大boss同意,游說(shuō)業(yè)務(wù)、產(chǎn)品、測(cè)試、開發(fā)各方一起協(xié)作。在保證業(yè)務(wù)規(guī)則和邏輯前提,進(jìn)行重大的重構(gòu)設(shè)計(jì),主要也是采用DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。

這次重構(gòu)經(jīng)歷了近6個(gè)月,頂著各方巨大的壓力。但經(jīng)過(guò)幾次重大升級(jí)發(fā)布,終于徹底改頭換面,神碼級(jí)舊系統(tǒng)完成改造。

1個(gè)java類近萬(wàn)行神碼經(jīng)過(guò)重構(gòu)改造,確切的說(shuō)應(yīng)該重設(shè)計(jì)重新寫代碼結(jié)構(gòu),總結(jié)一下有幾條寶貴經(jīng)驗(yàn):

  • 根據(jù)DDD讀寫分離設(shè)計(jì),寫入通過(guò)實(shí)現(xiàn)不同的Command執(zhí)行器,查詢實(shí)現(xiàn)不同的查詢執(zhí)行器。
  • 根據(jù)不同業(yè)務(wù)場(chǎng)景增加多個(gè)不同的擴(kuò)展點(diǎn),有效地解耦業(yè)務(wù)。
  • 復(fù)雜的業(yè)務(wù)規(guī)則引入規(guī)則引擎,把業(yè)務(wù)規(guī)則抽象成一條條可動(dòng)態(tài)編輯和可維護(hù)規(guī)則,并實(shí)現(xiàn)動(dòng)態(tài)加載和配置,而不是硬代碼。

經(jīng)過(guò)一頓猛烈改造設(shè)計(jì),新版本的代碼清爽多了!

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

把一萬(wàn)行的代碼直接搞成了360行左右!

舊代碼:

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

消滅了18層地獄式代碼

改造后新代碼結(jié)構(gòu):

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

查詢統(tǒng)一執(zhí)行器如下圖所示:

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

擴(kuò)展點(diǎn)抽象查詢器

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

擴(kuò)展點(diǎn)執(zhí)行器抽象類

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐

經(jīng)過(guò)一番猛操作改造之后,代碼簡(jiǎn)潔很多,變得可讀可維護(hù),從此世界清新很多,維護(hù)代價(jià)極大的變小!

?

5、問題與總結(jié)

軟件的世界里沒有銀彈!因此領(lǐng)域模型還是事務(wù)腳本沒有對(duì)錯(cuò)之分, 關(guān)鍵看是否合適:
  • 對(duì)于簡(jiǎn)單的業(yè)務(wù)場(chǎng)景,使用事務(wù)腳本,其優(yōu)點(diǎn)是簡(jiǎn)單、直觀、易上手
  • 對(duì)于復(fù)雜的業(yè)務(wù)場(chǎng)景,比較有效的治理辦法就是領(lǐng)域建模,在封裝業(yè)務(wù)邏輯的同時(shí),提升了對(duì)象的內(nèi)聚性和重用性,因?yàn)槭褂昧送ㄓ谜Z(yǔ)言,使得隱藏的業(yè)務(wù)邏輯得到顯性化表達(dá),使得復(fù)雜性治理成為可能。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)結(jié)構(gòu)非常清晰,適合復(fù)雜業(yè)務(wù)場(chǎng)景,代碼結(jié)構(gòu)清晰,代碼維護(hù)代會(huì)低很多。當(dāng)然也需要業(yè)務(wù)建模與抽象能力,與傳統(tǒng)面象數(shù)據(jù)開發(fā)方法有本質(zhì)的不同,需要轉(zhuǎn)變開發(fā)者的思維方法,對(duì)團(tuán)隊(duì)有一定學(xué)習(xí)成本。

要求開發(fā)者:
  • 有開發(fā)卓越軟件的激情和毅力
  • 渴望學(xué)習(xí)和進(jìn)步
  • 有能力理解軟件模式,并懂得如何應(yīng)用這些模式?
  • 有發(fā)掘不同的設(shè)計(jì)方法能力和耐性
  • 勇于改變現(xiàn)狀
  • 希望編寫出更好的代碼

?

到了這里,關(guān)于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD實(shí)際項(xiàng)目落地最佳實(shí)踐的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 快速理解DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)思想-基礎(chǔ)篇

    本文與大家一起學(xué)習(xí)并介紹領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Drive Design) 簡(jiǎn)稱DDD,以及為什么我們需要領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),它有哪些優(yōu)缺點(diǎn),盡量用一些通俗易懂文字來(lái)描述講解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),本篇并不會(huì)從深層大論述講解落地實(shí)現(xiàn),這些大家可以在了解入門后再去深層次學(xué)習(xí)探討或在后續(xù)進(jìn)階

    2024年02月10日
    瀏覽(23)
  • 【架構(gòu)】領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的幾種典型架構(gòu)介紹

    【架構(gòu)】領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的幾種典型架構(gòu)介紹

    我們生活中都聽說(shuō)了DDD,也了解了DDD,那么怎么將一個(gè)新項(xiàng)目從頭開始按照DDD的過(guò)程進(jìn)行劃分與架構(gòu)設(shè)計(jì)呢? 各種服務(wù) IAAS:基礎(chǔ)設(shè)施服務(wù),Infrastructure-as-a-service PAAS:平臺(tái)服務(wù),Platform-as-a-service SAAS:軟件服務(wù),Software-as-a-service 從圖中已經(jīng)可以很容易看出架構(gòu)的演進(jìn)過(guò)程,

    2024年02月11日
    瀏覽(17)
  • 萬(wàn)字長(zhǎng)文助你上手軟件領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) DDD

    萬(wàn)字長(zhǎng)文助你上手軟件領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) DDD

    最近看了一本書《解構(gòu)-領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》,書中提出了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)統(tǒng)一過(guò)程(DDDRUP),它指明了實(shí)踐 DDD 的具體步驟,并很好地串聯(lián)了各種概念、模式和思想。因此,我對(duì)書本內(nèi)容做了梳理、簡(jiǎn)化,融入自己的理解,并結(jié)合之前閱讀的書籍以及實(shí)踐經(jīng)驗(yàn),最終形成這篇文章。

    2024年02月08日
    瀏覽(23)
  • 軟件架構(gòu)演進(jìn)過(guò)程與微服務(wù)設(shè)計(jì)中的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)

    軟件架構(gòu)的演進(jìn)是一個(gè)不斷改進(jìn)和解決問題的過(guò)程。從傳統(tǒng)架構(gòu)到面向服務(wù)架構(gòu)(SOA),再到微服務(wù)架構(gòu),每個(gè)階段都帶來(lái)了新的技術(shù)和解決方案。而在微服務(wù)架構(gòu)中,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)起著至關(guān)重要的作用,它能夠提高系統(tǒng)的可擴(kuò)展性、可維護(hù)性和可理解性。本文將介紹軟件架

    2024年02月16日
    瀏覽(19)
  • 快速理解DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)思想-基礎(chǔ)篇 | 京東物流技術(shù)團(tuán)隊(duì)

    快速理解DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)思想-基礎(chǔ)篇 | 京東物流技術(shù)團(tuán)隊(duì)

    本文與大家一起學(xué)習(xí)并介紹領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Drive Design) 簡(jiǎn)稱DDD,以及為什么我們需要領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),它有哪些優(yōu)缺點(diǎn),盡量用一些通俗易懂文字來(lái)描述講解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),本篇并不會(huì)從深層大論述講解落地實(shí)現(xiàn),這些大家可以在了解入門后再去深層次學(xué)習(xí)探討或在后續(xù)進(jìn)階

    2024年02月09日
    瀏覽(26)
  • DDD技術(shù)方案落地實(shí)踐

    從接觸領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的初學(xué)階段,到實(shí)現(xiàn)一個(gè)舊系統(tǒng)改造到DDD模型,再到按DDD規(guī)范落地的3個(gè)的項(xiàng)目。對(duì)于領(lǐng)域驅(qū)動(dòng)模型設(shè)計(jì)研發(fā),從開始的各種疑惑到吸收各種先進(jìn)的理念,目前在技術(shù)實(shí)施這一塊已經(jīng)基本比較成熟。在既往經(jīng)驗(yàn)中總結(jié)了一些在開發(fā)中遇到的技術(shù)問題和解決方

    2024年02月05日
    瀏覽(31)
  • 【實(shí)踐篇】手把手教你落地DDD

    【實(shí)踐篇】手把手教你落地DDD

    常見的DDD實(shí)現(xiàn)架構(gòu)有很多種,如經(jīng)典四層架構(gòu)、六邊形(適配器端口)架構(gòu)、整潔架構(gòu)(Clean Architecture)、CQRS架構(gòu)等。架構(gòu)無(wú)優(yōu)劣高下之分,只要熟練掌握就都是合適的架構(gòu)。本文不會(huì)逐個(gè)去講解這些架構(gòu),感興趣的讀者可以自行去了解。 本文將帶領(lǐng)大家從日常的三層架構(gòu)

    2024年02月06日
    瀏覽(93)
  • DDD領(lǐng)域驅(qū)動(dòng)

    DDD領(lǐng)域驅(qū)動(dòng)

    我們經(jīng)常講技術(shù)為業(yè)務(wù)服務(wù),架構(gòu)設(shè)計(jì)需要對(duì)業(yè)務(wù)充分理解,在面向復(fù)雜的業(yè)務(wù)場(chǎng)景時(shí),會(huì)面臨諸多問題: 復(fù)雜系統(tǒng)設(shè)計(jì) :業(yè)務(wù)系統(tǒng)多、業(yè)務(wù)類型多、業(yè)務(wù)相互耦合,有沒有合適的方法來(lái)指導(dǎo)模塊的邊界開發(fā)? 多團(tuán)隊(duì)協(xié)同 :業(yè)務(wù)系統(tǒng)邊界劃分不清,系統(tǒng)間依賴復(fù)雜,往往一

    2024年02月09日
    瀏覽(20)
  • DDD[領(lǐng)域驅(qū)動(dòng)模型]

    DDD[領(lǐng)域驅(qū)動(dòng)模型]

    這是一種思想,不是一個(gè)工具。更多內(nèi)容前往 IT-BLOG Eric Evans 于 2004 年提出的一種軟件設(shè)計(jì)方法和理論。在應(yīng)用架構(gòu)的設(shè)計(jì)中, 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) DDD 占據(jù)著非常重要的位置,可以說(shuō) DDD 是應(yīng)用架構(gòu)設(shè)計(jì)的核心。 DDD 是一套綜合軟件系統(tǒng)分析和設(shè)計(jì)的面向?qū)ο蠼7椒ā?過(guò)去 系統(tǒng)

    2024年02月04日
    瀏覽(23)
  • DDD進(jìn)階_領(lǐng)域事件是什么?如何開展領(lǐng)域事件驅(qū)動(dòng)開發(fā)工作?

    DDD進(jìn)階_領(lǐng)域事件是什么?如何開展領(lǐng)域事件驅(qū)動(dòng)開發(fā)工作?

    DDD從入門到精通,系列文章傳送地址,請(qǐng)點(diǎn)擊本鏈接。 ? 目錄 一、什么是領(lǐng)域事件 二、如何識(shí)別領(lǐng)域事件 三、領(lǐng)域事件的數(shù)據(jù)一致性 四、領(lǐng)域事件分類 1、微服務(wù)內(nèi)的領(lǐng)域事件 2、微服務(wù)之間的領(lǐng)域事件 五、領(lǐng)域事件案例 六、領(lǐng)域事件總體架構(gòu)圖 1. 事件構(gòu)建和發(fā)布 2、事

    2024年02月15日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包