1

我需要的東西,我不能讓我的頭纏着關於服務/用例模式(部分DDD設計),我想在我的下一個(Laravel PHP)項目實施幫助。來自Repository的數據結構可用於所有目的?

一切似乎都很清楚。令人困惑的DDD的一部分是來自存儲庫的數據結構。人們似乎選擇存儲庫應該返回的數據結構(數組或實體),但它都有缺點。其中之一是表現看我過去的經歷。還有一個是你沒有簡單數據結構的接口(數組或簡單對象屬性)。

我將從解釋以前項目的經驗入手。這個項目存在缺陷,但是我從中學到了一些很好的優點,並希望在我的新項目中看到,但解決了一些設計錯誤。

以前的經驗

在過去,我已經建立一個網站,是用Kohana的框架和原則2 ORM(數據映射模式)API爲中心。流程是這樣的:

網站控制器→API客戶端(HMVC調用)→API控制器→自定義庫→學說2 ORM原生庫/實體經理

我的定製庫恢復使用Doctrine2 DQL普通陣列。 Doctrine2建議將數組結果數據用於只讀操作。是的,它使我的網站很好,很輕。 API控制器只是將數組數據轉換爲JSON。就那麼簡單。

在過去,我的公司創建的項目完全依賴加載的Doctrine2實體,這是我們因性能而感到後悔的事情。

我的REST API支持像
/api/users?include_latest_adverts=2&include_location=true
對用戶資源的查詢。 API控制器將include_location傳遞給直接包含位置關係的存儲庫。控制器讀取latest_adverts=2並調用廣告資源庫以獲取每個用戶的最新2個廣告。數組被返回。

例如第一用戶陣列:

[ 
    name 
    avatar 
    adverts [ 
     advert 1 [ 
      name 
      price 
     ] 
     advert 2 [ 
      …. 
     ] 
    ] 
] 

這被證明是非常成功的。我的整個網站使用的是API。添加一個新客戶端會非常容易,因爲API已經完全使用oauth生產了。整個網站運行。

但是這個設計也有缺陷。我的控制器仍然包含許多用於驗證,郵寄,參數或過濾器的邏輯,例如has_adverts=true,以便僅向用戶提供廣告。這意味着如果我創建了一個新的端口,就像全新的CLI界面一樣,由於所有驗證等原因,我將不得不復制很多這些控制器。但是如果我要創建一個新客戶端,則不會有重複。所以至少有一個問題解決了:-)

我的管理面板完全耦合到doctrine2存儲庫/實體管理器,以加快開發(排序)。爲什麼?因爲我的API只有特別功能的胖控制器(特別驗證,郵寄註冊等)。我將不得不重做或重構很多東西。因此,決定直接使用這些實體,仍然有一些清晰的編寫代碼的方式,而不是重寫所有的API控制器並將它們移動到服務(例如,對於站點& admin)。時間是解決我的設計錯誤的一個問題。

對於我的下一個項目,我希望所有代碼都能通過我自己的定製存儲庫和服務。一個好的分離流程。

新項目(使用DDD的想法),並與兩難數據結構

雖然我喜歡的是API爲中心的想法,我不希望我的下一個項目是在覈心API爲中心的,因爲我覺得相同的功能應該在沒有HTTP協議的情況下可用。我想用DDD的想法設計核心。

但我喜歡這個想法,使用一個只是作爲API講話並返回簡單數組的圖層。任何新港口的完美基礎,包括我自己的前端。我的想法是將我的服務類視爲API接口(返回數組數據),進行驗證等。我可以爲網站(註冊)專門提供服務,管理員或後臺進程使用普通服務。在某些管理案例中,對於簡單的CRUD編輯,無需任何服務,我可以直接使用存儲庫。控制器將非常薄。有了這個創建一個真正的REST API將只是一個問題,使用我的前端控制器類使用相同的服務創建新的控制器。

對於像業務規則這樣的內部邏輯來說,使用實體(清除接口)而不是來自存儲庫的數組會很有用。這樣我就可以從定義一些基於屬性的邏輯中獲益。但如果我將使用Doctrine2,並且我的存儲庫將始終返回實體,那麼我的應用程序將遭受巨大的性能影響!

一個數據結構確保性能,但沒有明確的接口,另一個確保清晰的接口,但在使用類似於Doctrine 2(現在或將來)的數據模式模式時性能較差。另外,我最終可能會遇到兩種令人困惑的數據類型。

我想類似這樣的流動東西:

控制器(薄)→UserService(含確認)→UserRepository(只是存儲)→雄辯ORM

爲什麼雄辯而不是Doctrine2?因爲我想堅持一下Laravel框架和社區中常見的東西。因此,我可以從第三方模塊中獲益,例如根據模型生成管理界面或類似內容(繞過我的DDD規則)。除了使用第三方模塊,我會設計我的核心內容,所以切換應該總是很容易,不會影響數據結構選擇或性能。

Eloquent是主動記錄模式。所以我會試圖將這些數據轉換爲像Doctrine2實體那樣的POPO。但是不...如上所述,與教條2實際模型會使系統非常胖。所以我又回到了簡單的數組。瞭解這一點對未來的這兩種以及任何其他實施都有效。

但感覺不好總是依賴數組。特別是在創建內部業務規則時。開發人員必須猜測數組的值,在他的IDE中沒有自動完成功能,不能像Entity類那樣使用特殊的方法。但是,處理數據的兩種方式也不好。或者我太完美了;)我想要一個清晰的數據結構!

構建接口和POPO意味着很多重複的工作。我需要將Eloquent模型(只是一個表映射器,而不是實體)轉換爲實現此接口的實體對象。所有是額外的工作。最後,我的最後一層就像一個API,因此再次將它轉換爲數組。這也是額外的工作。數組似乎又成交了。

看起來很容易讀入DDD和六角形。這似乎很邏輯!但實際上,我試圖堅持面向對象原則這個簡單的問題。我想使用數組,因爲它是唯一能夠100%確定我不依賴於任何模型選擇和從我的ORM查詢關於性能等選項的唯一方法,並且在將數組轉換爲視圖或API時沒有重複的工作。但是用戶數組看起來並沒有明確的合同。我想用這些模式加快我的項目速度,而不是減慢它們:-)因此,不是有許多轉換器的選項。

現在我讀了很多話題。一個使POPO的接口符合像Doctrine2這樣的適當實體可能會返回的內容,但是所有額外的工作都可以用於Eloquent。切換到Doctrine2應該相當容易,但會影響性能如此糟糕,或者需要將Doctrine2陣列數據轉換爲這些自己的實體接口。其他人選擇返回簡單的數組。

有人說服人們使用Doctrine2而不是Eloquent,但是他們忽略了Doctrine2很重的事實,並且您確實需要將數組結果用於只讀操作。

我們設計存儲庫是可以改變的嗎?不是因爲它只是通過設計「很好」。那麼,如果它對業績或重複工作產生如此巨大的影響,我們如何依靠完整的實體?即使只使用Doctrine2(耦合),也會由於其性能而出現同樣的問題!

所有的ORM實現都能夠返回數組,因此在那裏沒有重複的工作。很好的表現。但我們錯過了明確的合同。我們沒有陣列或類屬性的接口(作爲解決方法)...呃;)

難道我只是錯過了我們的編程語言中缺少的塊嗎?簡單數據結構上的接口

是否明智的做出所有數組並且擁有先進的業務邏輯與這些數組對話?因此沒有明確界面的類。任何預先計算的數據(通常會由實體方法返回)將位於定義Service類的數組鍵中。如果不是明智的,考慮到以上所有情況,還有什麼選擇?

如果有人在這個考慮到性能,不同ORM實現等的「領域」方面有豐富經驗的人可以告訴我他/她如何處理這個問題,我真的很感激嗎?

在此先感謝!

回答

0

我認爲你正在處理的是類似的事情,我正在努力。該解決方案我想最好的作品是:

  1. 實體/存儲庫
    • 使用,並通過各地的實體總是執行寫操作時(創建事物,更新的東西,刪除的東西,複雜的組合物)。
    • 有時你可能在做讀操作時,使用單位(當你預期讀可能需要不久後用於寫...即- > findById不久之後- >保存)。
    • 無論何時您正在使用一個實體(無論是寫還是讀),存儲庫都需要成爲該地點。你應該能夠告訴新開發者他們只能通過Entities和Repository存儲到數據庫。
    • 實體將具有表示某個域對象的屬性(它們多次用表的字段表示數據庫表,但並不總是)。它們也將包含域邏輯/規則(即驗證,計算),因此它們不是貧血症。如果您的實體需要幫助與其他實體進行交互(需要觸發其他事件),或者您只需要一個額外的位置來處理一些額外的域邏輯(執行存儲庫調用以檢查某些特定條件),那麼您可能還會有一些域服務。
    • 您的存儲庫將僅用於與實體一起工作。知識庫可以接受實體並與他們一起做一些持續性的工作。或者他們可以只接受一些參數,並對整個實體進行讀取/讀取。
    • 某些存儲庫將知道如何保存比其他更復雜的某些域對象。也許一個實體具有一個包含其他實體列表的實體,這些實體需要與主實體一起保存(如果需要,您可以深入瞭解有關聚合根的知識)。
    • Repositories的接口位於您的Domain層,但不是這些存儲庫的實際實現。這樣你就可以擁有一個Eloquent版本或者其他的東西。
  2. 其他查詢(表數據網關)
    • 這些查詢不會與實體工作。他們只會接受參數並返回像Arrays或POPO(Plain Old PHP Objects)這樣的東西。
    • 很多時候,您將需要執行不會很好地返回到單個實體的讀取。這些讀取通常更多用於報告(不適用於類似CRUD的操作,如將用戶讀入最終提交併保存的編輯表單)。例如,您可能有一個200行的JOINed數據報告。如果您使用了Repositiory並嘗試返回大型深度對象(所有關係都已填充,或者甚至是延遲加載),那麼您將遇到性能問題。而是使用Table Data Gatway模式。你只是在顯示數據,而不是真的需要OOP權力。輸出的數據可能包含ID,通過UI可以用來啓動對Repository持久性方法的調用。
    • 當您在開發應用程序時,如果遇到需要新的Read/Report查詢,請在Table Data Gatway文件夾中的某個類的某個類中創建一個新方法。您可能會發現您已經創建了一個類似的查詢,因此請參閱如何合併其他查詢。如有必要,使用一些參數來使網關方法的查詢更加靈活(特別是選擇列,排序順序,分頁等)。 儘管不會讓您的查詢過於靈活,但這是查詢構建器/ ORM出錯的地方!你需要在一定程度上限制你的查詢,如果你需要替換它們(可能是一個不同的數據庫引擎),那麼你可以很容易地理解允許的變化是什麼,不是。在靈活性(所以你有更多的DRY代碼)和約束(所以你可以優化/替換查詢)之間找到適當的平衡取決於你。
    • 您可以在您的域中創建服務來處理接收參數,然後將它們傳遞到Table Data Gateway,然後接收數組以進行更多的變異。這將使您的域邏輯保持在域中(並在存儲庫&表數據網關的基礎架構/持久層之外)。
    • 再次,就像存儲庫一樣,在您的域服務中使用接口,以便實現詳細信息遠離您的域層,並駐留在實際的Table Data Gateway文件夾中。
相關問題