簡體   English   中英

關於我們在具有多個客戶端的分布式應用程序中進行驗證的方法的思考

[英]Thoughts on our approach to validation in a distributed application with multiple clients

我到這里來聽聽您對到目前為止我們采用的驗證方法的想法。 我們仍處於開發過程的初期,因此我們仍然可以對其進行更改。 驗證對於此應用程序和我們的客戶非常重要,因此我們需要找到最佳方法。 讓我描述一下到目前為止我們所做的...

我們正在構建此應用程序,該應用程序將由不同的客戶端使用。 我們不控制所有客戶,因此對所有層的驗證都有嚴格的要求。 我們確實控制了一些客戶端應用程序,其中一個是由約100個用戶使用的WPF應用程序。 在此應用程序中,工作流程如下:

|                     Client                   |                                 Backend Service                             |
ViewModel -> ClientRepository -> ServiceClient -> Service (WCF) -> ApplicationService -> DomainModel -> Repository -> Database

我們認為以下是進行驗證的候選對象。

  • 客戶端:ViewModel驗證,用於使用必需的字段,長度等支持UI。
  • 后端:服務請求DTO驗證,因為我們不能依靠客戶端始終提供100%的有效值。
  • 后端:域模型實體驗證。 我們不希望我們的實體永遠處於無效狀態,因此執行操作時每個實體將包含不同的檢查。
  • 后端:數據庫驗證,例如約束失敗(FK,唯一性,長度等)

客戶端的ViewModel驗證非常明顯,對於我們自己的客戶端來說,在到達服務之前,應在其中糾正盡可能多的錯誤。 雖然不能說其他應用程序消耗了我們的服務,但是應該假定最壞的情況。

服務請求DTO應該首先針對第三方應用程序和我們自己的客戶中的錯誤進行驗證。 確保請求正確,可以防止稍后在處理請求時彈出錯誤,從而確保更有效的服務。 就像ViewModel驗證一樣,這取決於不同屬性的必填字段,長度和格式(例如電子郵件)。

域模型中的實體本身應確保它們始終具有完全有效的屬性/屬性,我們以Customer實體為例,正像這樣實現。

public class Customer : Entity
{
    private Customer() : base() { }

    public Customer(Guid id, string givenName, string surname)
        : this(id, givenName, null, surname) { }

    public Customer(Guid id, string givenName, string middleName, string surname)
        : base(id)
    {
        if (string.IsNullOrWhiteSpace(givenName))
            throw new ArgumentException(GenericErrors.StringNullOrEmpty, "givenName");
        if (string.IsNullOrWhiteSpace(surname))
            throw new ArgumentException(GenericErrors.StringNullOrEmpty, "surname");

        GivenName = givenName.Trim();
        Surname = surname.Trim();

        if (!string.IsNullOrWhiteSpace(middleName))
            MiddleName = middleName.Trim();
    }
}

現在,盡管這可以確保屬性有效,但是CustomerValidator類將對Customer類進行整體驗證,以確保其處於有效狀態並且不僅具有有效屬性。 CustomerValidator是使用FluentValidation框架實現的。 在將客戶對象提交到數據庫之前,在應用程序服務中調用它。

到目前為止,您如何看待我們的方法?

我有點擔心的是,到處都拋出異常。 例如上面的例子中的ArgumentException ,但是在對象的當前狀態下不允許調用某些方法的情況下,也會發生InvalidOperationException

希望這些異常將很少被拋出,因為服務請求DTO已通過驗證,因此我認為這可能還可以嗎? 例如,當驗證服務請求DTO時,永遠不要引發參數異常,除非驗證中出現錯誤。 因此,您可以說域模型中的這些參數檢查是額外的安全層。 另一方面,如果客戶端調用一個服務方法,該服務方法調用在Customer對象上當前狀態不可用的方法(因此它應該失敗),則可以引發InvalidOperationException

你怎么看? 如果一切正常,當出現故障時,如何通過WCF適當通知用戶? ArgumentExceptionInvalidOperationException還是包含錯誤列表的異常(使用CustomerValidator類驗證客戶對象后,ApplicationService拋出該異常)。 我是否應該以某種方式捕獲所有這些異常並將它們轉換為WCF拋出的一些常規故障異常,以便客戶端可以對此做出反應並通知用戶發生了什么?

我很想聽聽您對我們的方法的想法。 我們正在開始構建這個相當大的應用程序,我們真的想找到一種執行驗證的好方法。 在我們的應用程序中,有一些非常關鍵的部分,數據的正確性非常重要,因此驗證非常重要!

我個人的觀點是,域一致性應由域處理。 因此,無需使用CustomerValidator

至於例外情況,您應該考慮ArgumentNullException ,它們應該是普遍存在的語言的術語(有關更深入的說明,請參見http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot- the-ubiquitous-language.html )。

順便說一句,即使您以前所有的DTO都已經過驗證,也永遠不要從域中刪除正確的驗證。 業務不變量是其自身的責任。

至於性能:例外具有計算量,但是在我到目前為止看到的大多數DDD方案中,它們都不是問題。 特別是當命令來自人類時,它們不是問題。

編輯
驗證始終是域的責任。 以一個ISIN值對象為例:它由構造函數通過拋出適當的異常來確保自己的不變性 在一個編碼良好的域中,您不能保存無效對象的實例。 因此,您不需要任何驗證器即可累積錯誤。

同樣,只要且僅當工廠是獲取實例的唯一方法時,工廠才能確保業務不變量。 技術上的不變量,例如db列長度,應該不在范圍之內,因此工廠可能是它們的理想位置。 這也將具有啟用異常鏈接的優勢:SqlException對於客戶端沒有太多的表現力。

使用表現性異常,客戶只需要嘗試/捕獲他們可以處理的異常(請記住,向用戶呈現異常是處理異常的一種方式)。

數據進入系統很重要。 這可以采取動作參數(視圖模型)或服務層中參數的形式。 此時,您必須始終進行超級驗證(將所有內容設置為可空,然后禁止空值,檢查整數是否為負數,等等),並確保在這些入口點處所有內容都是100%正確的。 這樣,除非驗證是針對特定的邊緣情況,否則系統的其余部分不必擔心。 客戶端驗證很不錯,但是您絕對不能完全依賴它。 有時驗證可能會斷開連接。 此外,也不能保證正在調用您的操作的客戶端就是您認為的客戶端(例如,我們都已更改了url中的查詢參數以查看會發生什么情況)。

我發布的代碼的問題在於,此時您域中的數據可能有效或無效。 如果您始終在過程的外部執行驗證,則不必擔心。 而且,您永遠不會想知道:“我將驗證放在哪里?”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM