(此文章同時發表在本人微信公眾號“dotNET每日精華文章”,歡迎右邊二維碼來關註。) 題記:ABP框架對多租戶場景提供了很好的支持,內建了多租戶的處理機制,今天我們來深入解析一下這一特性。 最近在基於ABP框架(ASP.NET Boilerplate)開發了一個SaaS。所以接下來可能會時不時分享... ...
(此文章同時發表在本人微信公眾號“dotNET每日精華文章”,歡迎右邊二維碼來關註。)
題記:ABP框架對多租戶場景提供了很好的支持,內建了多租戶的處理機制,今天我們來深入解析一下這一特性。
最近在基於ABP框架(ASP.NET Boilerplate)開發了一個SaaS。所以接下來可能會時不時分享一下ABP方面的文章。今天來介紹一下ABP對多租戶提供的支持特性。
ABP簡介
ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程式的新起點,它旨在成為一個通用的WEB應用程式框架和項目模板。ASP.NET Boilerplate 基於DDD的經典分層架構思想,實現了眾多DDD的概念(但沒有實現所有DDD的概念)。ABP不僅架構設計和代碼寫的好,文檔也很全面詳實(這是一個開發框架被技術選型的基礎)。尤其國內的很多熱心朋友還整理了中文的資料和文檔,比如郭陽銘的系列文章(http://www.cnblogs.com/mienreal/p/4528470.html)和ABP框架中國小組的中文文檔(https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese)。
多租戶
通常SaaS都是需要多租戶支持的。維基百科對多租戶的解釋是:軟體多租戶是指一個軟體架構的實例軟體運行在一個伺服器上,但存在多個租戶。租戶是一組共用一個公共的用戶訪問特定許可權的軟體實例。多租戶架構,軟體應用程式旨在提供每個租戶專用的實例包括數據、配置、用戶管理、租戶個體功能和非功能屬性。多租戶與多實例架構,獨立的軟體實例代表不同的租戶操作。
多租戶一般涉及如下幾種場景:
- 多部署-多資料庫:即針對每個租戶獨立部署一套應用程式實例,每個實例對應一個資料庫。這種不算是真正的多租戶,不過對於在設計時沒有考慮多租戶的遺留系統採用這種部署方式不失為一種折中辦法。
- 單部署-多資料庫:只有唯一的一個應用程式實例,每個租戶分別連接不同的資料庫。
- 單部署-單資料庫:應用程式實例和資料庫都是一個。通過在需要隔離數據的數據表中加入一個類似TanantId或EnterpriseId來區分。
- 單部署-混搭資料庫:應用程式實例一個,但是資料庫根據情況,可以是單個或者多個。比如免費用戶放到一個資料庫中,高級用戶分別有自己的資料庫。
- 集群部署-單/多/混搭資料庫:應用程式的邏輯實例還是一個(只是為了高可用和性能部署為一個集群),然後對應的資料庫可以是單個、多個和混搭。
另外,除了針對租戶的資料庫以外,可能還需要一個全局的資料庫(稱之為主機資料庫)來保存全局範圍的配置數據。在單資料庫情況下,主機數據可能就和租戶數據放在一起(甚至同一個數據表中)。
ABP對多租戶的支持
上面提到的所有多租戶場景,在ABP都可以支持。只需要在啟動配置中啟用多租戶即可。
Configuration.MultiTenancy.IsEnabled = true;
當然,最常見的場景恐怕就是單部署-單資料庫,所以ABP中內置了處理TenantId的機制(通過介面IMustHaveTenant或IMayHaveTenant來實現)。實體實現了IMustHaveTenant介面,會包含一個不能為空的TenantId屬性,即意味著其中的資料庫需要基於TenantId來進行隔離。實現了IMayHaveTenant介面,會包含一個能為空的TenantId屬性,在TenantId為空的時候代表數據屬於主機範圍的,不為空的時候表示數據基於租戶來隔離。
而ABP通過一個特殊封裝的IAbpSession來給使用者提供當前TenantId的獲取,如果是主機用戶登錄系統,那麼TenantId就是為空的,否則就是登錄用戶所在租戶的Id。
ABP在多租戶下讀取數據
ABP並非只是簡單的給你的實體類添加一個TenantId屬性,而是通過識別IMustHaveTenant或IMayHaveTenant介面,使用數據過濾機制(根據底層所用ORM不同有不同的實現方式)自動在你讀取數據的時候,基於當前AbpSession中的TenantId來過濾數據。也就是說,你查詢讀取數據的時候,寫“where item.TenantId == AbpSession.TenantId” 這樣的代碼是毫無必要的。
需要註意的是,如果實體實現的是IMustHaveTenant介面,且AbpSession.TenantId為null的時候(即主機用戶),獲取到的數據是所有租戶的,除非你自己顯式進行過濾。而在IMayHaveTenant情況下,AbpSession.TenantId為null獲取到的是主機用戶的數據。
ABP在多租戶下寫入數據
在多租戶的情形下,寫入數據也通過攔截機制(比如重寫DbContext的SaveChanges方法),可以自動為你的實體設置TenantId屬性,不管你用的是IMustHaveTenant還是IMayHaveTenant。雖然官方文檔是推薦在創建實體的時候,總是顯示設置TenantId的,尤其在使用IMayHaveTenant的時候(這也是abp使用者唯一需要關係這個屬性的地方)。但是,就我個人的看法而言,利用框架的原因就是為了讓編碼簡單,所以我還是傾向於建議大家不用顯式設置TenantId。
ABP切換租戶
最後,ABP也提供一系列機制讓你在代碼中切換tenant(包括租戶與主機間的切換)。關於多租戶的官方文檔(http://www.aspnetboilerplate.com/Pages/Documents/Multi-Tenancy)最後的內容也詳細講到了切換租戶的一些最佳實踐。