從壹開始微服務 [ DDD ] 之二 ║ DDD入門 & 專案結構粗搭建
前言
哈嘍大家好,今天是週二,我們的DDD系列文章今天正式開始講解,我這兩天一直在學習,也一直在思考如何才能把這一個系列給合理的傳遞給大家,並且達到學習的目的,還沒有特別好的路線,只是一個大概的模糊的安排,畢竟我沒有做過講師,但是我感覺還是需要對自己負責,至少要對得起這個熬夜寫的部落格吧 ?,我簡單設計了下整體流程,可能以後還會變動,不過大致方向是不會變的:
我打算通過一個最簡單一個例子來講,而且這個例子也是《實現領域驅動設計》這一本書的第一個小例子,至於為什麼要引用這個小栗子,我想的是,希望把整本書的大致精髓柔和到我這個 Project 中,最好給大家一個感覺,以後大家再看的的時候,不會感覺在天上飄飄然不知其所往,相反,是會感覺似曾相識,會感覺看過,不會那麼生硬無趣,這個就是我希望達到的兩個目的的其中之一:在瞭解並認識DDD領域驅動設計的情況下,也簡單的瞭解了這邊書的相關內容。
今天要說的是我們平時百分之百會遇到的,就是新建一個實體類,並對其進行CURD操作,看到這裡,你也許會胸有成竹的說,已經玩兒的很溜了,當然這是肯定的,今天不是說哪些新知識,而且說一下其實DDD驅動設計,已經在我們平時的開發中體現出來,當然又沒有完全貫徹下來(至少我是這麼一個),也說一下在我們的開發當中為什麼需要使用DDD領域驅動設計。
這裡說明下:今天搭建的是一個小雛形,我會以後慢慢細化,像上一個系列那樣,逐漸增加功能,差不多最後的框架是這樣的:
零、今天要實現橙色的部分
一、為什麼要使用DDD
這個已經是老生常談了,相信大家也稍微或多或少的瞭解一些,在領域驅動設計中,我們的中心不在程式碼是開發技術上,而且在業務上,在這個設計中,會涉及到開發人員和領域專家(可能是專案管理者,或者部門經理,甚至是公司總經理,這些都可以算上領域專家),雖然在使用DDD的時候,需要我們在前期投入較多的時間和精力去考慮如何建模,已經開發過程中會遇到的各種問題,到那時這樣的投入是完全值得的,相信這個系列的結束,大家有一箇中肯的評價。
1、會增加我們專案的業務價值
在開發中,什麼是最寶貴的,當然是開發人員的時間效率最重要,然後還有就是溝通上,如果我們自己自定義的開發出一套程式碼,沒有一系列的業務模組,這樣的話,就是一個純技術的專案,雖然本來領域專家也看不懂,但是如果我們僅僅專注於技術方面,而沒有把業務人員的思想所對映到開發者中,那整個專案也只有可能僅僅是你成為這個領域專家,因為只有開發者當事人才能看懂。
這樣隨著時間的發展,隨著開發者當事人的離職或者轉向其他專案,本應該駐留在軟體中的領域知識也就丟失了。在以後的任何修改程式碼中,新的開發人員雖然可以重新和領域專家進行思想的溝通,但是之前開發的程式碼已經無法撼動,這也就是為什麼現在我們找工作的時候,不喜歡修改別人的程式碼,而且也會經常聽到這個情況:儘量不要修改原來的程式碼,自己新建一個,嗯,這個更是要命了的。
試想一下,如果我們把注意力重點放到了業務上,這樣就很清晰的進行迭代,而且也可以進一步的與領域專家進行對接。那我們的專案中都需要用到 DDD 領域驅動設計麼?答案當時是否定的,如果滿足以下幾點,我建議還是需要使用 DDD 吧:
1、如果你的系統中有30~40個使用者故事或者用例流的時候,軟體的複雜性就出來了。
這裡說明下使用者故事和用例流:就比如一個商城,我們有普通使用者,會員,公司管理員,企業合作伙伴,站長等等多個角色,當然還有其他的角色,每一個角色又會有一系列的操作,比如使用者會瀏覽商品,下單,修改個人資訊等等一系列的操作,這個就是用例流,這些角色所進行的全部操作,就是使用者故事。
2、如果你的系統現在不是很複雜,但是以後會複雜。就比如我們的商城後臺:本來是僅僅的對資料庫表的CURD,但是在真正的使用者(管理員)使用的時候,會發現在商品商家的時候,並不是很方便,需要用到價格規格表,或者發現商家資訊已經實現商品的多對多分配(現在可能是一對多),那想想後期的修改是很龐大的。
3、如果你的系統所在的領域不是很清晰,就連領域專家也不是很清晰,需要在未來幾年的慢慢討論,變化中前進的時候。
2、貧血症和失憶症
相信大家都聽過這兩個名詞,雖然聽著不是很舒服,但是卻天天在我們的手中被設計出來,如果你說不會,那好,請看下邊兩個情況是否在你的系統中出現(這裡說明下,這些栗子都是我現在手中的專案):
1、你的領域物件(就是指實體類物件)是不是主要包含些共有的get 和 set,並且幾乎沒有業務邏輯?比如這樣:
public class BlogArticle
{
public int bID { get; set; }
public string bsubmitter { get; set; }
public string btitle { get; set; }
public string bcategory { get; set; }
public string bcontent { get; set; }
public int btraffic { get; set; }
public int bcommentNum { get; set; }
public DateTime bUpdateTime { get; set; }
public System.DateTime bCreateTime { get; set; }
public string bRemark { get; set; }
}
2、你的軟體元件(就是指我們的方法定義)中是否經常會用到領域物件來實現一些業務邏輯,而且還是通過 get 和 set 的方式,直接將其拋給服務層或者應用層,甚至是直接拋到頁面上。
//新增一個車票
public ActionResult AddTicket(FormCollection form)
{ var TitleHead = form["TitleHead"].ToString(); var txtSubmitter = form["txtSubmitter"].ToString(); var TicketChannelOperateJson = form["TicketChannelOperateJson"].ToString(); try { if (!string.IsNullOrEmpty(TitleHead) && !string.IsNullOrEmpty(TicketChannelOperateJson) && !string.IsNullOrEmpty(txtSubmitter))
{
Tickets tickets = new Tickets
{
Submiter = txtSubmitter,
SubmitDate = DateTime.Now,
SubmitData = TicketChannelOperateJson,
ActionType = Common.Enums.ActionType.Add,
ActionStatus = Common.Enums.ActionStatus.New,
Approver = "",
LastUpdateDate = DateTime.Now,
IsDelete = false,
IsCleanUp = false,
IsCheckIn = false,
TicketType = TicketType.Channel,
};
TicketsBLL ticketsBLL = new TicketsBLL(); bool flag = ticketsBLL.Add(tickets) > 0;
}
} catch (Exception e)
{
Logger.Log(e.InnerException + e.Message);
}
}
如果兩個情況你都說的是 NO ,那麼恭喜你,你的程式碼設計很健康,沒有檢查出來有貧血問題,如果你的回答中有一個 YES,那是不存在的,如果是兩個YES,我們就請繼續往下看吧。
二、貧血物件對我們做了什麼
如果你上邊的都已經看明白了,那我們就開始建我們的專案了,當然這裡要說明下,以後會對專案進行修繕,前幾章的程式碼可能很 low ,甚至是不好的,只是為了能說明問題。
1、新建一個 .net core web 專案
在指定的資料夾下,新建一個 Christ3D 解決方案,然後再新建一個 web 專案,具體過程相信大家都會了,如果不會,請會看第一個系列《系列教程一目錄:.netcore+vue 前後端分離》
2、在Models 資料夾中,新增我們的領域物件 Customer.cs 和持久化虛擬類 CustomerDao.cs
/// <summary>
/// Customer 領域物件
/// </summary>
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime BirthDate { get; set; }
}
/// <summary>
/// 領域物件持久化層
/// </summary>
public class CustomerDao
{
public static Customer GetCustomer(string id)
{
return new Customer() { Id = "1", Name = "Christ", Email = "Christ@123.com" };
}
public static string SaveCustomer(Customer customer)
{
return "儲存成功";
}
}
3、在預設的 HomeController.cs 控制器中,新增儲存顧客方法 saveCustomer()
/// <summary>
/// 儲存顧客方法:add & update /// </summary>
/// <param name="id"></param>
/// <param name="name"></param>
/// <param name="email"></param>
/// <param name="birthDate"></param>
public void saveCustomer(string id, string name, string email, string birthDate)
{
Customer customer = CustomerDao.GetCustomer(id); if (customer == null)
{
customer = new Customer();
customer.Id = id;
} if (name != null)
{
customer.Name = name;
} if (email != null)
{
customer.Email = email;
} //...還有其他屬性
CustomerDao.SaveCustomer(customer);
}
到這裡,這就是一個領域 —— 對顧客領域物件進行儲存,這個功能還是很強大的,不管一個 Customer 是新增還是更新,甚至是不管名字換了,還是郵件重新申請了,都可以在這裡體現,相信這樣的方法我們都寫過,至少我是這樣。
這個方法真的很強大麼,這裡我們看一下,我們並不知道 saveCustomer() 的具體的應用場景(是後臺管理,還是前臺使用者自己操作,還是更新手機號等等),經過幾周或者幾年後,我們完全不知道當時建立這個方法的本來目的和意圖,當然你會說可以通過註釋的方法,我表示是反對的,我們對程式的設計,不能依賴於註釋,這是很可怕的。
還有,這些方法巢狀在一起,我們不能對其進行單元測試,都不用說細節,可能資料庫的約束就能對這個方法造成崩潰:比如時間欄位,比如字串長度過長,比如不能為空等,而且因為有很多地方呼叫這個看似功能強大的基方法,我們需要一個一個的往上游去研究,看看到底是哪個地方,哪個方法,哪個業務邏輯呼叫了,可能需要幾個小時的時間,甚至更久。
總結來說,上邊的 saveCustomer() 方法存在三個弊端:
1、saveCustomer() 方法的業務意圖不明確,當時我們為了貪圖功能的強大,把注意力都放到了技術上,從而忽略了業務核心。
2、saveCustomer() 方法的實現本來就增加了潛在的複雜性,雖然看著強大了,可是複雜度卻直線向上。
3、Customer 顧客領域物件根本不是物件,充其量就是一個資料持有者,僅僅是把我們的資料從持久化的資料庫中拿出來到內容的一個工具。
那既然如此,我們需要怎麼辦呢,沒錯,就是我們平時使用到的分層,一個專注領域業務的分層 —— DDD領域驅動設計,就出現了。
三、一切皆從領域開始 —— Domain
還記得下邊這個圖麼,這個是我上一篇文章中提到的《領域驅動設計架構圖》,我個人表示,這個圖已經很貼切和較為詳細的表示了DDD領域驅動設計是整體框架圖,大家從整體的箭頭走向就能看清楚(誰指向誰,表示前者依賴後者,後者實現前者),其中的內容我們都會說到,今天先簡單的把這個整體框架搭起來,至少先從什麼的DDD框架講起來——當然也就是從被依賴最多的領域層開始。
從上邊的文章中,我們知道了,在軟體開發中,我們已經把重點放到領域業務上,在上邊的 saveCustomer() 的方法中,所有的邏輯和用例都一股腦的放在一起,完全背離了這個思想,所以,那我們如果想要通過領域設計的思路來建立,會是怎麼樣的呢,請往下看。
1、定義領域物件 Customer.cs(值物件/聚合/根,以後會說)
在解決方案中,新建 .net core 類庫 Christ3D.Domain ,作為我們的領域層(這是一個臃腫的領域層,以後我們會把領域核心給抽象出來,現在簡化是為了說明),然後在該層下,新建 Models 資料夾,存放我們以後的全部領域物件,我們的專業領域設計,都是基於領域物件為基礎。
老張:這裡並沒有增加業務邏輯,以後會說明
/// <summary>
/// 定義領域物件 Customer /// </summary>
public class Customer
{ protected Customer() { } public Customer(Guid id, string name, string email, DateTime birthDate)
{
Id = id;
Name = name;
Email = email;
BirthDate = birthDate;
} public Guid Id { get; private set; } public string Name { get; private set; } public string Email { get; private set; } public DateTime BirthDate { get; private set; }
}
2、定義泛型介面 IRepository.cs
這裡說下為什麼開發中都需要介面層:
在層級結構中,上層模組呼叫下層模組提供的服務,這裡就會存在一種依賴關係,Rebort C. Martin提出了依賴倒置原則大致是如下:
上層模組不應該依賴於下層模組,兩者都應該依賴於抽象;
抽象不應該依賴於實現,實現應該依賴於抽象;
這是一個面向介面程式設計的思想。
在我們的領域層下,新建 Interfaces 資料夾,然後新增泛型介面
在我們專注的領域業務中,我們只需要定義該領域Customer 的相關用例即可(就比如如何CURD,如何發郵件等等,這些都是使用者角色Customer的用例流),而不用去關心到底是如何通過哪種技術來實現的,那種ORM去持久化的,這就是領域設計的核心,當然現在有很多小夥伴還是喜歡直接把介面和實現放在一起,也無可厚非,但是不符合DDD領域設計的思想。
可能這個時候你會說,領域層,定義介面和實現方法放在一起也可以嘛,現在我們是看不出來效果的,以後我們會在這裡說到領域驅動,領域通知,事件驅動等等知識點的時候,你就會發現,在Domain層來對介面進行實現是那麼格格不入,沒關係慢慢來~~~
/// <summary>
/// 定義泛型倉儲介面,並繼承IDisposable,顯式釋放資源 /// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface IRepository<TEntity> : IDisposable where TEntity : class { /// <summary>
/// 新增 /// </summary>
/// <param name="obj"></param>
void Add(TEntity obj); /// <summary>
/// 根據id獲取物件 /// </summary>
/// <param name="id"></param>
/// <returns></returns>
TEntity GetById(Guid id); /// <summary>
/// 獲取列表 /// </summary>
/// <returns></returns>
IQueryable<TEntity> GetAll(); /// <summary>
/// 根據物件進行更新 /// </summary>
/// <param name="obj"></param>
void Update(TEntity obj); /// <summary>
/// 根據id刪除 /// </summary>
/// <param name="id"></param>
void Remove(Guid id); /// <summary>
/// 儲存 /// </summary>
/// <returns></returns>
int SaveChanges();
}
3、定義 Customer 領域介面 ICustomerRepository.cs
我們以後的每一個子領域中特有的介面,還是需要定義的,並且繼承自我們的泛型倉儲介面
/// <summary>
/// ICustomerRepository 介面 /// 注意,這裡我們用到的業務物件,是領域物件 /// </summary>
public interface ICustomerRepository : IRepository<Customer> { //一些Customer獨有的介面
Customer GetByEmail(string email);
}
好了,這個時候我們的最最最簡單的領域層就搭建好了,裡邊有我們的子領域 Customer的相關介面實現,整體結構是這樣的:
四、基礎層對領域進行實現 —— Infrastructure
上邊我們們定義了 Domian 領域層,這是一個介面層,那我們必須來實現它們,大家可以再往上看那個DDD領域驅動設計架構圖,你應該能找到,是誰對領域層進行了實現,答案當然是基礎設施層。
我們在解決方案下,新建一個 Christ3D.Infrastruct.Data 類庫,你一定會問,為什麼不直接是 Christ3D.Infrastruct ,反而在後邊還需要多一個 .Data 字尾呢,這裡要說的就是,在這個基礎設施層中,會有很多很多的內容,比如驗證層,IoC層,事務,工作單元等等,以後都會說到,至少從名字上你也能明白——基礎設施,這些基礎東西是不能放在領域層的,這是肯定的,因為我們關心的是業務領域,這個不是我們的業務,也不會是下文的應用層,至於為什麼,你可以先想一想,資料驗證和AOP這些為何不放在應用層。
1、新建泛型倉儲 Repository
Repository繼承了IRepository介面,這裡我們先不寫具體的實現,我們先定義好方法體,以後再慢慢填上,大家還記得如何快速的實現介面吧,Ctrl+. 可能有的小夥伴沒有這個功能,那就只能手動了。
/// <summary>
/// 泛型倉儲,實現泛型倉儲介面
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
public void Add(TEntity obj)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public IQueryable<TEntity> GetAll()
{
throw new NotImplementedException();
}
public TEntity GetById(Guid id)
{
throw new NotImplementedException();
}
public void Remove(Guid id)
{
throw new NotImplementedException();
}
public int SaveChanges()
{
throw new NotImplementedException();
}
public void Update(TEntity obj)
{
throw new NotImplementedException();
}
}
2、實現子領域Customer 倉儲
/// <summary>
/// Customer倉儲,操作物件還是領域物件 /// </summary>
public class CustomerRepository : Repository<Customer>, ICustomerRepository
{ //對特例介面進行實現
public Customer GetByEmail(string email)
{ throw new System.NotImplementedException();
}
}
最終結構
相信大家看到這裡,基本還是很輕鬆的,我們在領域層,把業務定義清楚,把領域物件設計好,然後在基礎層對其進行實現,是一個很好的過程,這麼看還是可以的。
但是一定會有小夥伴會看不慣這麼寫,他會說,領域設計定義介面我懂,那就定義,定義實現也可以,比如用 EFCore 或者其他 ORM 框架,那這樣直接在展示層呼叫不就好了,為啥還需要單拿出來一個應用 Application 層呢(等同於我們之前的 Service 層),到時候肯定又多出來一套介面和實現的過程,麻煩不麻煩?
我在上一個系列教程中,本來也是這麼嘗試使用DDD領域驅動設計,可以中間沒有看《實現領域驅動設計》這本書,導致出現了漏洞,也被各種小夥伴吐槽,這個系列就再證明一下吧,具體有什麼好處,或者說為什麼要在基礎設施層之上,再增加一個應用層(也就是我們的 Service 層),這裡先不說,下邊請繼續看。
五、定義系統的業務功能 —— Application
如果Repository 應用在應用層,會出現什麼情況:這樣就致使應用層和基礎層(我把資料持久化放在基礎層了)通訊,忽略了最重要的領域層,領域層在其中起到的作用最多也就是傳遞一個非常貧血的領域模型,然後通過 Repository 進行“CRUD”,這樣的結果是,應用層不變成所謂的 BLL(常說的業務邏輯層)才怪,另外,因為業務邏輯都放在應用層了,領域模型也變得更加貧血。
Application為應用層(也就是我們常說的 Service 層),定義軟體要完成的任務,並且指揮表達領域概念的物件來解決問題。這一層所負責的工作對業務來說意義重大,也是與其它系統的應用層進行互動的必要渠道。應用層要儘量簡單,不包含業務規則或者知識,而只為下一層中的領域物件協調任務,分配工作,使它們互相協作。它沒有反映業務情況的狀態,但是卻可以具有另外一種狀態,為使用者或程式顯示某個任務的進度。
1、檢視模型——Rich 領域模型(DTO以後說到)
在文章的最後,我們們再回顧下文章開頭說的貧血物件模型,相信你應該還有印象,這個就是對剛剛上邊這個問題最好的回答,如果我們直接把展示層對接到了基層設施層,那我們勢必需要用到領域模型來操作,甚至是對接到檢視裡,不僅如此,我們還需要驗證操作,傳值操作等等,那我們又把領域物件模型過多的寫到了業務邏輯裡去,嗯,這個就不是DDD領域驅動設計了,所以我們需要一個應用層,對外進行資料介面的提供,這裡要強調一點,千萬不要把應用層最後寫滿了業務邏輯,業務應該在領域層!!!
在專案根路徑下,新建 Christ3D.Application 類庫,作為我們的應用層,然後新建 ViewModels 資料夾,用來存放我們的基於UI 的檢視模型,它是如何來的,這個下邊說到。
/// <summary>
/// 子領域Customer的檢視模型 /// </summary>
public class CustomerViewModel
{
[Key] public Guid Id { get; set; }
[Required(ErrorMessage = "The Name is Required")]
[MinLength(2)]
[MaxLength(100)]
[DisplayName("Name")] public string Name { get; set; }
[Required(ErrorMessage = "The E-mail is Required")]
[EmailAddress]
[DisplayName("E-mail")] public string Email { get; set; }
[Required(ErrorMessage = "The BirthDate is Required")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
[DataType(DataType.Date, ErrorMessage = "Data em formato inválido")]
[DisplayName("Birth Date")] public DateTime BirthDate { get; set; }
}
這裡僅僅是增加了特性,更多的業務邏輯還是在 領域層 來實現的。
2、定義應用服務介面 ICustomerAppService,依賴抽象思想
在我們的應用層下,新建 Interfaces 資料夾,用來存放我們的對外服務介面,然後新增 Customer服務介面類,這裡要說明下,在應用層對外介面中,我們就不需要定義泛型基類了,因為已經沒有必要,甚至是無法抽象的,
/// <summary>
/// 定義 ICustomerAppService 服務介面
/// 並繼承IDisposable,顯式釋放資源
/// 注意這裡我們使用的物件,是檢視物件模型
/// </summary>
public interface ICustomerAppService : IDisposable
{
void Register(CustomerViewModel customerViewModel);
IEnumerable<CustomerViewModel> GetAll();
CustomerViewModel GetById(Guid id);
void Update(CustomerViewModel customerViewModel);
void Remove(Guid id);
}
3、實現應用服務介面 CustomerAppService.cs ,對接基層設施層
在我們的應用層下,新建 Services 資料夾,用來存放我們對服務介面的實現類
/// <summary>
/// CustomerAppService 服務介面實現類,繼承 服務介面
/// 通過 DTO 實現檢視模型和領域模型的關係處理
/// 作為排程者,協調領域層和基礎層,
/// 這裡只是做一個面向使用者用例的服務介面,不包含業務規則或者知識
/// </summary>
public class CustomerAppService : ICustomerAppService
{
private readonly ICustomerRepository _customerRepository;
public CustomerAppService(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
public IEnumerable<CustomerViewModel> GetAll()
{
return null;
//return _customerRepository.GetAll().ProjectTo<CustomerViewModel>();
}
public CustomerViewModel GetById(Guid id)
{
return null;
//return _mapper.Map<CustomerViewModel>(_customerRepository.GetById(id));
}
public void Register(CustomerViewModel customerViewModel)
{
//var registerCommand = _mapper.Map<RegisterNewCustomerCommand>(customerViewModel);
}
public void Update(CustomerViewModel customerViewModel)
{
//var updateCommand = _mapper.Map<UpdateCustomerCommand>(customerViewModel);
}
public void Remove(Guid id)
{
//var removeCommand = new RemoveCustomerCommand(id);
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
目前這裡還沒有具體使用基礎層的倉儲,為什麼呢,因為應用層是面向檢視物件模型,不涉及到業務,而基礎設施層和領域層是基於 領域物件模型,面向業務的,所以我們需要用到 DTO ,這一塊以後我們會說到。
六、結語
好啦,今天的講解基本就到這裡了,到目前為止,僅僅是實現了DDD領域驅動設計的第一個 D 領域模型,還沒有說到驅動的概念,通過經典的DDD四層,大家應該也瞭解了各層的作用,這裡有簡單的三個問題,不知道你是否已經真的看懂了,如果都能回答上來,恭喜!如果不是很確定,那抱歉,還需要再看看,或者查資料看書,或者來群裡諮詢我吧。
1、什麼是貧血物件模型?
2、我們的業務介面和業務實現,分別在哪一層?( Answer:領域層和基礎設施層 )
3、為什麼還需要定義一個應用層,而不是直接在應用層(Service層)對接基礎層(Repository層)?
好啦,週四我們會繼續推進,DDD 領域物件設計是如何實現 領域、子域、界限上下文的
七、Github & Gitee
相關文章
- 從壹開始微服務 [ DDD ] 之八 ║剪不斷理還亂的 值物件和Dto微服務物件
- Axon框架快速入門和DDD專案實踐框架
- DDD之1微服務設計為什麼選擇DDD微服務
- webpack入門級 - 從0開始搭建單頁專案配置Web
- 函式式DDD架構入門 - SCOTT WLASCHIN函式架構
- 【DDD/CQRS/微服務架構案例】在Ubuntu 14.04.4 LTS中執行WeText專案的服務端微服務架構Ubuntu服務端
- 教你玩轉微服務--基於DDD的微服務架構落地實踐之路微服務架構
- 實施DDD的幽默:DDD落地需要專門的框架嗎?框架
- 能顯示業務目標的DDD微服務架構圖 -Aleix微服務架構
- .Net Core微服務入門全紀錄(一)——專案搭建微服務
- 可落地的DDD(4)-如何利用DDD進行微服務的劃分(2)微服務
- 當DDD遇到專案管理專案管理
- ddd-crew/ddd-starter-modelling-process:DDD設計入門建模流程
- DDD設計模式結構圖設計模式
- .NET雲原生應用實踐(一):從搭建專案框架結構開始框架
- 從零開始搭建架構實施Android專案架構Android
- 05 常見微服務專案結構微服務
- 阿里DDD專案最佳實踐-COLA 架構總覽阿里架構
- 從MVC到DDD的架構演進MVC架構
- Java進階專題(二十二) 從零開始搭建一個微服務架構系統 (上)Java微服務架構
- 從零開始搭建一個vue專案Vue
- 從零開始搭建vue.js專案Vue.js
- .NET Core/.NET5/.NET6 開源專案彙總6:框架與架構設計(DDD、雲原生/微服務/容器/DevOps/CICD等)專案框架架構微服務dev
- DDD興起的原因以及與微服務的關係微服務
- 從零開始的Android新專案(1):架構搭建篇Android架構
- 到底什麼是微服務?其實就是DDD領域服務微服務
- hyperf從零開始構建微服務(一)——構建服務提供者微服務
- hyperf從零開始構建微服務(二)——構建服務消費者微服務
- 基於COLA架構建立運輸微服務應用和DDD領域建模架構微服務
- 微前端:DDD和微服務對客戶端開發的好處 - thenewstack前端微服務客戶端
- 領域驅動設計(DDD)入門&概要
- 從 Flutter 2.0 開始學 - 實踐、專案搭建Flutter
- 從零開始React專案架構(六)React架構
- 從零開始React專案架構(三)React架構
- 從零開始React專案架構(四)React架構
- 從零開始React專案架構(五)React架構
- 從零開始React專案架構(一)React架構
- 從零開始React專案架構(二)React架構