iT邦幫忙

DAY 6
0

第一次設計架構就上手系列 第 6

第一次設計架構就上手 - 同中求異

同中求異

我們所要面臨的第一道難題,也是多租戶系統架構的最大難題 - 怎樣的系統架構才能符合表面上是一套系統,實際上卻能適應各家養雞場的客製化要求?而且需要易於擴充修改,方便維護,又不會增加開發人員額外的工作呢?

解決方案一

通常對於如何同中求異,最直覺的解決方案便是使用判斷式了,像是...

if (User.IsChickenFarm('Aaa'))
{
    balabala...
}
else if (User.IsChickenFarm('Baa'))
{
    balabala...
}

有開發經驗的人應該都吃過這種寫法的苦頭(XD),在某幾段程式裡偶爾為之還可以,要是系統裡的所有商業邏輯的地方都有這種判斷式,日後若運氣不好需要再新增其他家養雞場時,就得先找出所有的判斷式,再將判斷式的程式碼全改過,根據莫非定律「凡是可能出錯的事必定會出錯」,再怎麼謹慎仔細,一定會有不小心漏改的地方。另外這種寫法十分容易引發開發人員心中的小惡魔,寫完第一家養場雞後,將所以的程式碼複製貼上到第二家的段落法,然後再修改當中差異的部份。結果便是有一大堆的程式碼,但是當中有八九成的部份都是相同的。

解決方案二

對於這種需要動態判斷並抽換商業邏輯的架構,目前最火紅的技術非DI(Dependency injection, 依赖注入)的架構莫屬了,透過將不同的商業邏輯抽取成相同的抽象介面,並藉由替換容器內的物件實作,來達到動態切換不同程式實作。例如我們可以先將商業邏輯抽取出介面。(這邊使用Autofac Container來做說明)

public interfase IChickenService
{
    void DoService();
}

public class FarmAaaChickenService : IChickenService
{
    public void DoService() { ... }
}

public class FarmBbbChickenService : IChickenService
{
    public void DoService() { ... }
}

接著在使用者登入時先針對不同的養雞場來初始化ContainerBuilder。

var builder = new ContainerBuilder();

if (User.IsChickenFarm('Aaa'))
{
    builder.RegisterType<FarmAaaChickenService>().As<IChickenService>();
}
else if (User.IsChickenFarm('Bbb'))
{
    builder.RegisterType<FarmBbbChickenService>().As<IChickenService>();
}

之後,只要統一透過Container來取得IChickenService的實作,便無需考慮目前使用者規屬的養雞場,並且能夠在相同的程式碼中自動取得正確的商業邏輯實作。

var container = builder.Build();

using (var scope = container.BeginLifetimeScope())
{
    var service = scope.Resolve<IChickenService>();
    service.DoService();
}

或是透過注入的方式於建構時將物件做為參數傳入,Autofac會自動從Container產生正確的實例並且注入建構式。

public class Demo
{
    private IChickenService service;
    public Demo(IChickenService service)
    {
        this.service = service;
    }

    public void Method1()
    {
        this.service.DoService();
    }
}

未完待續...


上一篇
第一次設計架構就上手 - 用Identity存放使用者資料
下一篇
哈哈, 週未果然是鐵人賽的殺手...
系列文
第一次設計架構就上手7

尚未有邦友留言

立即登入留言