PowerBI DAX MVC 設(shè)計(jì)模式 導(dǎo)論
本文更像是一篇白話(huà)了的論文,也希望如此,你可以點(diǎn)擊閱讀原文以關(guān)注本文的更新。
我們一直在考慮的其實(shí)是一個(gè)終極問(wèn)題:到底如何最大限度復(fù)用。在 PowerBI 中處理這類(lèi)問(wèn)題主要涉及兩件事:業(yè)務(wù)邏輯的處理以及可視化的處理。在實(shí)踐中常常表現(xiàn)為以下問(wèn)題:
- 客戶(hù)需求變化了,怎么辦?
- 幾十頁(yè)的報(bào)告,上百的度量值怎么管理?
- 刪除一個(gè)可視化圖表時(shí),為它而做的度量值是否可以安心刪除?(如果其他地方在用就不能刪除,但你不知道)
- 為了做一個(gè)圖,要做一個(gè)輔助表;那為了做100個(gè)圖呢,輔助表巨多,怎么管理?
- 如果客戶(hù)有一個(gè)展現(xiàn)原型,是否可以對(duì)著展現(xiàn)原型分工制作報(bào)告?
- 如果某個(gè)度量值發(fā)生變化,如何確保與其關(guān)聯(lián)的度量值以及可視化都是正確的?
- 諸如:對(duì)某類(lèi)別按子類(lèi)別市場(chǎng)所選元素的產(chǎn)品銷(xiāo)售占有率 這種很長(zhǎng)的邏輯命名如何處理?
- …
這些問(wèn)題隨著制作的 PowerBI 文件逐漸復(fù)雜,也就越來(lái)越嚴(yán)重,嚴(yán)重到需要:推倒重來(lái)。也就是說(shuō),到底應(yīng)該如何正確地設(shè)計(jì) PowerBI 報(bào)告,不會(huì)出現(xiàn)推倒重來(lái),且可以可持續(xù)可應(yīng)對(duì)變化地迭代推進(jìn)復(fù)用呢?
這個(gè)問(wèn)題才是作為 PowerBI 設(shè)計(jì)師需要思考的終極問(wèn)題。當(dāng)然,這在其他領(lǐng)域也很常見(jiàn),如:建筑領(lǐng)域,軟件工程領(lǐng)域等。那些領(lǐng)域更加成熟,也有著成熟的解決方案,而我們大可以將其他領(lǐng)域的成果移植到 PowerBI 領(lǐng)域,而本文正是一次這樣的實(shí)踐。之所以稱(chēng)為導(dǎo)論,是因?yàn)樵谶@個(gè)導(dǎo)論下,我們會(huì)持續(xù)展開(kāi)出很多細(xì)節(jié),包括各種原則,各種套路等。
引子
羅叔在以前的很多文章中冒出過(guò)以下字眼:設(shè)計(jì)模式,非侵入式設(shè)計(jì),MVC 模式,開(kāi)放閉合原則,依賴(lài)注入等。這些其實(shí)都從屬于一個(gè)體系:設(shè)計(jì)模式。本文將作為一個(gè)體系的開(kāi)篇,給出一個(gè) 設(shè)計(jì)模式 的框架:PowerBI DAX MVC 設(shè)計(jì)模式。MVC 模式并不是用來(lái)解決用戶(hù)做一個(gè)圖的問(wèn)題,而是用來(lái)系統(tǒng)化解決自助數(shù)據(jù)建模的整個(gè)工程流程,尤其是面對(duì)N頁(yè)(如:50頁(yè)),多種業(yè)務(wù)計(jì)算(如:上百度量值)的情況。
設(shè)計(jì)模式(Design Pattern),在軟件領(lǐng)域是一個(gè)非常重要的概念。而 MVC 模式,可以說(shuō)是模式的模式,本文將論述如何在 PowerBI 中實(shí)現(xiàn) MVC 設(shè)計(jì)模式。MVC 設(shè)計(jì)模式具有非常強(qiáng)的通用性,此前,我們很多設(shè)計(jì)都采用了 MVC 設(shè)計(jì)模式,以保證 PowerBI 的建模設(shè)計(jì)可以應(yīng)對(duì)變化,保持穩(wěn)定。
雖然 MVC 設(shè)計(jì)模式來(lái)自于軟件領(lǐng)域,也不是面向業(yè)務(wù)人員的,但羅叔就是要將復(fù)雜的概念簡(jiǎn)化,并讓業(yè)務(wù)人員可以生搬硬套,并逐漸體會(huì)設(shè)計(jì)模式的好處。注意,這里并非學(xué)習(xí)筆記,也不是翻譯,作為科班出身的羅叔,將站在不同領(lǐng)域前人基礎(chǔ)上,基于成熟的軟件工程,面向?qū)ο笏枷?,PowerBI 大量實(shí)踐總結(jié)后,推出這套原創(chuàng)體系。首先,羅叔會(huì)在公眾號(hào)分享這個(gè)系列,相關(guān)的文件和視頻講解會(huì)作為訂閱會(huì)員的內(nèi)容。隨后,我們會(huì)推出一個(gè)專(zhuān)題課程,更加系統(tǒng)性地講解這個(gè)體系,從一定意義上說(shuō),可以深刻理解設(shè)計(jì)模式并運(yùn)用于 PowerBI DAX,將達(dá)到的狀態(tài)是:對(duì)于任何一個(gè)業(yè)務(wù)問(wèn)題,如果 PowerBI DAX 可以解,那設(shè)計(jì)師就可以做出來(lái);反之,如果設(shè)計(jì)師無(wú)法按照設(shè)計(jì)模式實(shí)現(xiàn)某個(gè)問(wèn)題,那該問(wèn)題也就不是 PowerBI DAX 可以解的。是不是夠牛X的,是不是吹牛,讓我們從本文慢慢開(kāi)始體會(huì)吧。
關(guān)于設(shè)計(jì)模式
作為業(yè)務(wù)人員,可能這是一個(gè)新鮮的概念;而作為開(kāi)發(fā)人員,設(shè)計(jì)模式幾乎是一個(gè)分水嶺,理解并使用設(shè)計(jì)模式標(biāo)志了開(kāi)發(fā)進(jìn)入了一個(gè)正確的軌道。如果沒(méi)有聽(tīng)說(shuō)過(guò)設(shè)計(jì)模式,也一定聽(tīng)說(shuō)過(guò)商業(yè)模式。從本質(zhì)上來(lái)看,設(shè)計(jì)模式與商業(yè)模式一樣,指的是某種場(chǎng)景下重復(fù)出現(xiàn)某類(lèi)問(wèn)題的成熟應(yīng)對(duì)方案。也就是人們常說(shuō)的套路。
關(guān)于設(shè)計(jì)模式的更多描述可以參考:維基百科:設(shè)計(jì)模式
在 PowerBI DAX 領(lǐng)域,由兩位意大利老師(馬克·啰嗦以及法拉利)在 2015 年給出了一些參考,并形成了一個(gè)網(wǎng)站以及一本書(shū):https://www.daxpatterns.com/

該書(shū)很有意義,即使是現(xiàn)在也有很重要的參考意義。另外,兩位意大利老師應(yīng)該會(huì)重寫(xiě)該書(shū),具體發(fā)布時(shí)間未知。
不過(guò)這并不重要,羅叔將會(huì)在戰(zhàn)友聯(lián)盟平臺(tái)相繼推出 PowerBI DAX 設(shè)計(jì)模式 系列。我們這里的設(shè)計(jì)模式絕非對(duì)意大利老師所提及的設(shè)計(jì)模式的簡(jiǎn)單參考或重復(fù),而是全新的更加透徹的思想提煉,你將在全網(wǎng)范圍首次看到如何將軟件工程領(lǐng)域的成熟思想和解決方案平滑地在 PowerBI DAX 中實(shí)現(xiàn),本文將讓你看到雖然也許你已經(jīng)學(xué)習(xí)了很多 PowerBI 課程,但一切才剛剛開(kāi)始。能夠駕馭本文所提出的問(wèn)題以及涉及到的概念并幫助我們成為更加專(zhuān)業(yè)的設(shè)計(jì)師。
設(shè)計(jì)模式,作為白話(huà)理解的套路,就像下棋的棋譜:

作為棋譜,它會(huì)告訴你在某種場(chǎng)景下,每一步要怎么走,就是說(shuō)套路并不是一招,而是一步一步的招數(shù)的相繼組合。那么,DAX 設(shè)計(jì)模式也不是一個(gè)函數(shù),而是為了解決某個(gè)場(chǎng)景的函數(shù)的連續(xù)使用方案。
那么,最重要的問(wèn)題來(lái)了,設(shè)計(jì)模式有什么好處?作為棋譜或者套路,設(shè)計(jì)模式是幫助你可以解決重復(fù)出現(xiàn)的類(lèi)似問(wèn)題的通用解決方案。就類(lèi)似于,中學(xué)時(shí)的方程,一上來(lái)管它三七二十一,設(shè)X,然后再說(shuō)。設(shè)計(jì)模式,將給出你在 PowerBI DAX 中幾乎所有問(wèn)題的通用解決方式。更為重要的是,這里面所提出的一些原則,不僅僅適用于軟件領(lǐng)域,PowerBI 報(bào)表設(shè)計(jì)領(lǐng)域,還適用于平時(shí)的企業(yè)管理等,你這需要領(lǐng)會(huì)精神就可以舉一反三。
MVC 設(shè)計(jì)模式
MVC 模式(Model–View–Controller)來(lái)源于軟件工程中的一種軟件架構(gòu)模式,把軟件系統(tǒng)分為三個(gè)基本部分:模型(Model)、視圖(View)和控制器(Controller)。羅叔沒(méi)有必要展開(kāi)讓大家去理解軟件工程,但如果你用過(guò) PowerBI,你會(huì)馬上發(fā)現(xiàn),在 PowerBI 中使用 MVC模式 是自然而然的。
先來(lái)具體說(shuō)明下什么是 PowerBI 中的使用 DAX 時(shí)的 MVC,一個(gè)示意圖如下:

- 控制器(Controller):即度量值,本質(zhì)是計(jì)算邏輯。
- 視圖(View):用戶(hù)可以看到的可視化部分。
- 模型(Model):與分析主題相關(guān)的主體數(shù)據(jù)表及關(guān)系。
為了可以更加形象地理解這個(gè)過(guò)程,我們先看一個(gè)案例:

這是一個(gè)典型的 PowerBI 報(bào)告,從表面看不出什么不同,其不同在于它的打造方式是按照 MVC 模式來(lái)進(jìn)行的,如下:

不難看出,這里使用了典型的 PowerBI 內(nèi)置的一些構(gòu)建元素,如:表,度量值,度量值文件夾,然后其組織方式是不同尋常的。
- 在 #Controllers 中,放置了與業(yè)務(wù)邏輯有關(guān)的度量值。
- 在 #Views 中,放置了與視覺(jué)展現(xiàn)有關(guān)的度量值。
- 以 Model_ 前綴開(kāi)頭的表,構(gòu)成了主體數(shù)據(jù)模型。
以上三點(diǎn)是約定,羅叔在設(shè)計(jì)這套模式的時(shí)候本著:約定勝于配置的原則。這些并不是制作 PowerBI 報(bào)告時(shí)候的強(qiáng)制要求,但羅叔發(fā)現(xiàn)這些的習(xí)慣和約定在設(shè)計(jì)大型 PowerBI 項(xiàng)目時(shí)非常受用。
在 PowerBI 的2019年8月 更新后,由于視覺(jué)對(duì)象編組的出現(xiàn),將 MVC 設(shè)計(jì)模式的實(shí)際可行性變得更強(qiáng),我們看下它們是如何結(jié)合的:

可以看出,在某個(gè)頁(yè)面中的 Chart 與度量值的對(duì)應(yīng)關(guān)系,如下:

這樣,很清晰地講可視化與呈現(xiàn)可視化的度量值完成的對(duì)應(yīng)。
PowerBI 工程
在真實(shí)的大型項(xiàng)目中(非常常見(jiàn)),目前沒(méi)有任何教程來(lái)指導(dǎo)如何套路式地開(kāi)展一個(gè)大型 PowerBI 報(bào)告項(xiàng)目。首先,羅叔根本不認(rèn)為 PowerBI 是一個(gè)報(bào)告,也許那是其他 BI 報(bào)告工具的合適稱(chēng)呼,但在 PowerBI 身上并不適用。我們稱(chēng)一個(gè) PowerBI 文件為一個(gè) PowerBI 工程。
而 MVC 架構(gòu)設(shè)計(jì),正是為了流暢地處理這個(gè)工程的套路。先來(lái)看下一個(gè) PowerBI 工程(報(bào)告)的組成部分。如下:

這應(yīng)該是你首次見(jiàn)到這樣的 PowerBI 工程模型,我們依次講解如下:
- PowerBI 工程:與一個(gè) PowerBI 文件對(duì)應(yīng)。這是一個(gè)邏輯概念,從一個(gè)很小的報(bào)告文件到一個(gè)很大很復(fù)雜的報(bào)告文件,都可以是工程,在應(yīng)對(duì)大型 PowerBI 文件時(shí),用工程的思路將更有助于設(shè)計(jì)師完成好的設(shè)計(jì)以及持續(xù)的改進(jìn)和管理維護(hù),應(yīng)對(duì)變化。一般地,一個(gè) PowerBI 工程應(yīng)該集中解決一類(lèi)業(yè)務(wù)問(wèn)題。
- PowerBI 報(bào)告:PowerBI 的可見(jiàn)部分,被包含在一個(gè) PowerBI 文件中。
- PowerBI 頁(yè)面:PowerBI 報(bào)告的組成部分,一個(gè) PowerBI 報(bào)告內(nèi)含多個(gè) PowerBI 頁(yè)面。
- PowerBI 可視化元素:又稱(chēng) PowerBI 可視化對(duì)象,是可以獨(dú)立管理的最小可見(jiàn)元素,一個(gè) PowerBI 頁(yè)面內(nèi)含多個(gè) PowerBI 可視化元素。如:圖表,表格,切片器,圖形,按鈕等。
- 數(shù)據(jù)模型:需要解決業(yè)務(wù)問(wèn)題所需要的表及其關(guān)系。注意:數(shù)據(jù)模型與業(yè)務(wù)直接相關(guān),而與展示無(wú)關(guān)。
- 數(shù)據(jù)模型表:加載進(jìn)入數(shù)據(jù)模型的表。一個(gè)數(shù)據(jù)模型中有多個(gè)數(shù)據(jù)模型表。如:訂單表。
- 度量值:處理數(shù)據(jù)的計(jì)算邏輯。分為處理業(yè)務(wù)邏輯以及處理展現(xiàn)邏輯。如:銷(xiāo)售額,銷(xiāo)售額(考慮總計(jì)行)。
- DAX計(jì)算-業(yè)務(wù)邏輯:為處理業(yè)務(wù)邏輯而定義的DAX表達(dá)式,用來(lái)處理業(yè)務(wù)問(wèn)題。如:銷(xiāo)售額。
- DAX計(jì)算-展現(xiàn)邏輯:為處理展現(xiàn)邏輯而定義的DAX表達(dá)式,用來(lái)處理展現(xiàn)問(wèn)題。即:DAX驅(qū)動(dòng)可視化。如:銷(xiāo)售額(考慮總計(jì)行)。
- 視圖模型:需要解決展現(xiàn)問(wèn)題所需要的表及其關(guān)系。注意:視圖模型與展現(xiàn)直接相關(guān),而與業(yè)務(wù)無(wú)關(guān)。
- 視圖表:為輔助展現(xiàn)而定義的表。一個(gè)視圖模型中有多個(gè)視圖表。如:年齡分組。
- 參數(shù)表:為輔助計(jì)算而定義的表。一個(gè)視圖模型中有多個(gè)參數(shù)表。如:計(jì)算單位參數(shù)表。
雖然 PowerBI 的構(gòu)件本身是很少的,而為了解決實(shí)際的很多問(wèn)題,羅叔將這有限的物理構(gòu)件做了體系化的邏輯劃分。這樣就可以組織并管理不同的目的進(jìn)而解決不同的問(wèn)題。
上述對(duì)于 PowerBI 工程以及邏輯組成部分的定義是精確的,值得一提的是:
- 這些定義并非是微軟官方的,但經(jīng)過(guò)幾年實(shí)踐與觀察,這是符合微軟設(shè)計(jì)理念且可持續(xù)的。
- 這是對(duì)我們此前提出各種設(shè)計(jì)模式概念的首次正式定義。
- 值得強(qiáng)調(diào)的是:與其他BI工具不同,在 PowerBI 中,將由 DAX 驅(qū)動(dòng)可視化來(lái)完成復(fù)雜的可視化設(shè)計(jì)。
- 值得強(qiáng)調(diào)的是:為了配合 DAX 驅(qū)動(dòng)可視化,需要有與之搭配的數(shù)據(jù),我們稱(chēng)之為視圖模型。
在有了這些定義后,羅叔進(jìn)而帶你更加仔細(xì)地來(lái)理解這里面的設(shè)計(jì)思想。
DAX MVC
根據(jù)前面的描述,可以有:

其中,箭頭表示依賴(lài)關(guān)系,為了便于理解,你可以讀作:會(huì)用到。例如:度量值會(huì)用到數(shù)據(jù)模型;可視化會(huì)用到數(shù)據(jù)模型和度量值。
可以看出,這樣的設(shè)計(jì)有一個(gè)非常大的好處,那就是:數(shù)據(jù)模型 不依賴(lài)于任何其他部分,這也就是羅叔以前所說(shuō)過(guò)的非侵入式設(shè)計(jì)。將這個(gè)架構(gòu)更加細(xì)分可以得到:

如果考慮到數(shù)據(jù)源和角色,還可以表示為:

我們將這里涉及到的角色也加入進(jìn)來(lái),可以看到:
- 數(shù)據(jù)模型依賴(lài)于數(shù)據(jù)源,從數(shù)據(jù)源加載數(shù)據(jù)便可以得到數(shù)據(jù)模型;
- 業(yè)務(wù)度量值僅依賴(lài)于數(shù)據(jù)模型,定義業(yè)務(wù)邏輯;
- 對(duì)于最終的可視化來(lái)說(shuō),數(shù)據(jù)模型也許不是直接來(lái)源,而是視圖模型,例如:年齡分組,就不是數(shù)據(jù)模型中的表,而完全是為了展現(xiàn)而創(chuàng)建的數(shù)據(jù),屬于視圖模型;
- 為了展現(xiàn)而寫(xiě)的度量值,就是展現(xiàn)度量值,例如:如果增長(zhǎng)就是綠色,否則就是紅色等。它依賴(lài)于數(shù)據(jù)模型、數(shù)據(jù)模型和業(yè)務(wù)度量值;
- 最終的可視化由視圖模型和展現(xiàn)度量值構(gòu)成。
如果考慮下角色,可以看到:
- 數(shù)據(jù)分析師更側(cè)重按照業(yè)務(wù)邏輯構(gòu)建數(shù)據(jù)模型和業(yè)務(wù)度量值;
- 報(bào)表設(shè)計(jì)師會(huì)和用戶(hù)溝通,用戶(hù)說(shuō)我要看到什么樣的報(bào)告,報(bào)表設(shè)計(jì)師則實(shí)現(xiàn)用戶(hù)的需求;
- 大部分情況下,數(shù)據(jù)分析師 和 報(bào)表設(shè)計(jì)師是重合的。
值得強(qiáng)調(diào)的是,可以注意這里面的依賴(lài)關(guān)系,這種依賴(lài)關(guān)系,可以形成良好的層次結(jié)構(gòu)。上層的內(nèi)容總是依賴(lài)于下層的內(nèi)容,這樣,當(dāng)用戶(hù)的需求改變時(shí),不需要更改下層的內(nèi)容,而僅僅需要修改上層的內(nèi)容即可。這樣就將更改限制在了一個(gè)有限的范圍內(nèi)。
如何在 PowerBI 中實(shí)現(xiàn) DAX MVC
有了上述的理論討論和說(shuō)明,現(xiàn)在可以在 PowerBI 中進(jìn)行實(shí)現(xiàn)。
數(shù)據(jù)模型
首先,按照常規(guī)操作,在 PowerBI 中構(gòu)建數(shù)據(jù)模型,如下:

除非你非常清楚自己在干什么,否則請(qǐng)務(wù)必遵守以下規(guī)則:
- 保持表之間的關(guān)系是單向的一對(duì)多關(guān)系。
- 不同的主題可以用不同的布局來(lái)表示。

作為一個(gè)約定,可以將數(shù)據(jù)模型的表名命名為:Model_表名。
業(yè)務(wù)度量值
作為 Controller 的最組成部分,首先我們需要有個(gè)業(yè)務(wù)度量值表。并將所有業(yè)務(wù)度量值在其中排列,如下:

業(yè)務(wù)度量值也會(huì)構(gòu)成一個(gè)依賴(lài)層級(jí)關(guān)系,例如:

視圖模型與展現(xiàn)度量值
如上所述,視圖模型與數(shù)據(jù)模型最大的不同在于視圖模型是為了可視化而存在的,視圖模型與展現(xiàn)度量值同時(shí)使用,這種結(jié)合特別可以反映 MVC 模式的特點(diǎn):

其中,
- View.AgeGroup 表就不是數(shù)據(jù)模型表,它僅僅是為了顯示年齡分組而存在的,而年齡分組是與業(yè)務(wù)邏輯沒(méi)有關(guān)系的,屬于視圖層面。
- 由于該圖放置于報(bào)告的第三頁(yè)(P3)且為該頁(yè)的第一個(gè)圖(C1),講放置于 #View 中的展現(xiàn)度量值命名為:View.P3.C1.Sales.ByAgeGroup 也正反應(yīng)了這個(gè)事實(shí)。
值得注意的是,這里的 展現(xiàn)度量值 就開(kāi)辟了 DAX 驅(qū)動(dòng)可視化的思想,在無(wú)法用常規(guī)方式形成所需要的展現(xiàn)時(shí),一個(gè)固定套路就是,將需要的樣子依賴(lài)的維度放置在那,然后編寫(xiě)展現(xiàn)度量值即可。
視圖
在 PowerBI DAX MVC 模型的視圖與軟件工程中的視圖有所不同,DAX構(gòu)建的視圖是用戶(hù)拖拽產(chǎn)生的 DAX查詢(xún)而形成,它僅僅依賴(lài)于兩個(gè)物件:維度 與 度量值。其中,維度來(lái)自于視圖模型(或數(shù)據(jù)模型),而度量值來(lái)自于展現(xiàn)度量值。
如果僅僅顯示銷(xiāo)售額,也可以使用展現(xiàn)度量值進(jìn)行包裝,例如:
View.P1.C2.Value = [KPI.Sales]
這樣做的好處是,當(dāng)某個(gè)可視化對(duì)象被刪除時(shí),與之相關(guān)的展現(xiàn)度量值也可以被刪除,這就解決了海量業(yè)務(wù)邏輯度量值可能會(huì)出現(xiàn)的模型管理問(wèn)題。讓視圖僅僅依賴(lài)于視圖模型和展現(xiàn)度量值,就解除了這種耦合關(guān)系。
總結(jié)
本文引出了 PowerBI DAX MVC 架構(gòu)設(shè)計(jì)模式,其用意是徹底工程式地解決復(fù)用問(wèn)題。
- 控制器:將度量值看做是控制器,控制計(jì)算邏輯;
- 控制業(yè)務(wù)邏輯的,叫業(yè)務(wù)度量值;
- 控制展現(xiàn)邏輯的,叫展現(xiàn)度量值;
- 模型:將數(shù)據(jù)模型進(jìn)一步分為:
- 數(shù)據(jù)模型,僅僅指業(yè)務(wù)數(shù)據(jù)模型;
- 視圖模型,用來(lái)展現(xiàn)所需要的輔助數(shù)據(jù);
- 視圖:用戶(hù)看到的可視化部分;
- 依賴(lài)于視圖模型,為視圖提供維度;
- 依賴(lài)于展現(xiàn)度量值,為視圖提供計(jì)算結(jié)果。
既然我們考慮的終極問(wèn)題是復(fù)用,MVC 架構(gòu)設(shè)計(jì)模式更多的是為我們提供了一個(gè)框架,由于 PowerBI 本身并沒(méi)有提供特定于 MVC 模式的支持,但我們通過(guò)約定的方式,仍然可以實(shí)現(xiàn)這一架構(gòu)。以 MVC 架構(gòu)開(kāi)始,我們將不斷進(jìn)一步融入其他可復(fù)用思想,包括但不限于:抽象,單一職責(zé)原則,開(kāi)放閉合原則,里氏代換原則,接口隔離原則,依賴(lài)反轉(zhuǎn)原則以及面向接口和非侵入式設(shè)計(jì)思想,這些內(nèi)容我們將逐步展開(kāi),祝各位老鐵玩得開(kāi)心。