2012-02-27 25 views
9

Tôi là người mới đến với SOA mặc dù tôi có một số kinh nghiệm trong OOAD.“Không sử dụng lớp Abstract Base trong thiết kế; nhưng trong Mô hình/Phân tích ”

Một trong các nguyên tắc cho thiết kế SOA là “Chỉ sử dụng lớp trừu tượng để tạo mô hình. Bỏ chúng từ thiết kế ”. Việc sử dụng trừu tượng có thể hữu ích trong mô hình hóa (giai đoạn phân tích).

Trong giai đoạn phân tích, tôi đã tìm ra một lớp cơ sở BankAccount. Các lớp chuyên biệt bắt nguồn từ nó là “FixedAccount” và “SavingsAccount”. Tôi cần tạo một dịch vụ sẽ trả lại tất cả các tài khoản (danh sách tài khoản) cho người dùng. Cấu trúc của (các) dịch vụ phải đáp ứng yêu cầu là gì?

Lưu ý: Thật tuyệt vời nếu bạn có thể cung cấp mã trình diễn bằng cách sử dụng WCF.

+0

Nếu đây là bài tập về nhà, vui lòng gắn thẻ như vậy. –

+0

@HenkHolterman Nó không phải là một bài tập về nhà. Tôi được phân bổ vào một dự án mới sử dụng SOA. Mặc dù trước đây tôi đã sử dụng WCF, nhưng nó không sử dụng SOA. Tôi đang cố gắng tìm hiểu các khái niệm SOA. – Lijo

Trả lời

8

Có vẻ như bạn đang cố gắng sử dụng SOA để truy cập từ xa mô hình đối tượng của bạn. Bạn sẽ tốt hơn khi xem xét các tương tác và khả năng mà bạn muốn dịch vụ của mình hiển thị và tránh phơi bày chi tiết thừa kế của việc triển khai dịch vụ của bạn.

Vì vậy, trong trường hợp này, nơi bạn cần một danh sách các tài khoản người dùng giao diện của bạn sẽ giống như

[ServiceContract] 
interface ISomeService 
{ 
    [OperationContract] 
    Collection<AccountSummary> ListAccountsForUser(
     User user /*This information could be out of band in a claim*/); 
} 

[DataContract] 
class AccountSummary 
{ 
    [DataMember] 
    public string AccountNumber {get;set;} 
    [DataMember] 
    public string AccountType {get;set;} 
    //Other account summary information 
} 

nếu bạn quyết định đi xuống con đường thừa kế, bạn có thể sử dụng KnownType attribute, nhưng lưu ý rằng điều này sẽ thêm một số loại thông tin vào tin nhắn được gửi qua dây mà có thể giới hạn khả năng tương tác của bạn trong một số trường hợp.

Cập nhật:

tôi là một chút giới hạn cho thời gian trước khi tôi trả lời, vì vậy tôi sẽ cố gắng và xây dựng trên lý do tại sao tôi thích phong cách này.

Tôi sẽ không khuyên bạn để lộ OOAD thông qua DTO trong một lớp riêng biệt, điều này thường dẫn đến giao diện cồng kềnh nơi bạn truyền tải rất nhiều dữ liệu không được sử dụng và phân loại bản đồ của mô hình miền của bạn với tất cả logic đã bị xóa và tôi không thấy giá trị. Tôi thường thiết kế lớp dịch vụ của mình xung quanh các hoạt động mà nó hiển thị và tôi sử dụng các DTO để định nghĩa các tương tác dịch vụ.

Sử dụng DTO dựa trên hoạt động được hiển thị và không phải trên mô hình miền giúp giữ gói dịch vụ và giảm khớp nối với mô hình miền. Bằng cách không phơi bày mô hình miền của mình, tôi không phải thực hiện bất kỳ thỏa hiệp nào về khả năng hiển thị trường hoặc thừa kế vì lợi ích của việc tuần tự hóa.

ví dụ nếu tôi đã phơi bày một phương pháp chuyển từ một tài khoản khác giao diện dịch vụ sẽ giống như thế này:

[ServiceContract] 
interface ISomeService 
{ 
    [OperationContract] 
    TransferResult Transfer(TransferRequest request); 
} 

[DataContract] 
class TransferRequest 
{ 
    [DataMember] 
    public string FromAccountNumber {get;set;} 
    [DataMember] 
    public string ToAccountNumber {get;set;} 
    [DataMember] 
    public Money Amount {get;set;} 
} 

class SomeService : ISomeService 
{ 
    TransferResult Transfer(TransferRequest request) 
    { 
     //Check parameters...omitted for clarity 
     var from = repository.Load<Account>(request.FromAccountNumber); 
     //Assert that the caller is authorised to request transfer on this account 
     var to = repository.Load<Account>(request.ToAccountNumber); 
     from.Transfer(to, request.Amount); 
     //Build an appropriate response (or fault) 
    } 
} 

bây giờ từ giao diện này nó là rất rõ ràng đối với conusmer những gì các dữ liệu cần thiết để gọi thao tác này là. Nếu tôi đã triển khai điều này là

[ServiceContract] 
interface ISomeService 
{ 
    [OperationContract] 
    TransferResult Transfer(AccountDto from, AccountDto to, MoneyDto dto); 
} 

và tài khoảnDưới là bản sao của các trường trong tài khoản, là người tiêu dùng, tôi nên điền vào các trường nào? Tất cả bọn họ? Nếu một thuộc tính mới được thêm vào để hỗ trợ một hoạt động mới, tất cả người dùng của tất cả các hoạt động bây giờ có thể thấy thuộc tính này. WCF cho phép tôi đánh dấu tài sản này là không bắt buộc để tôi không phá vỡ tất cả các khách hàng khác của mình, nhưng nếu nó là bắt buộc đối với hoạt động mới, khách hàng sẽ chỉ tìm ra khi họ gọi cho hoạt động.

Tệ hơn nữa, với tư cách là người triển khai dịch vụ, điều gì xảy ra nếu họ đã cung cấp cho tôi số dư hiện tại? tôi có nên tin tưởng không?

Quy tắc chung ở đây là hỏi ai sở hữu dữ liệu, khách hàng hoặc dịch vụ? Nếu khách hàng sở hữu nó, thì nó có thể chuyển nó cho dịch vụ và sau khi thực hiện một số kiểm tra cơ bản, dịch vụ có thể sử dụng nó. Nếu dịch vụ sở hữu nó, khách hàng chỉ nên chuyển đủ thông tin cho dịch vụ để truy xuất thông tin cần thiết. Điều này cho phép dịch vụ duy trì tính nhất quán của dữ liệu mà nó sở hữu.

Trong ví dụ này, dịch vụ sở hữu thông tin tài khoản và khóa để định vị nó là số tài khoản. Mặc dù dịch vụ có thể xác thực số tiền (là số tiền dương, tiền được hỗ trợ, v.v.) do khách hàng sở hữu và do đó chúng tôi hy vọng tất cả các trường trên DTO sẽ được điền.

Tóm lại, tôi đã thấy nó được thực hiện theo 3 cách, nhưng thiết kế DTO xung quanh các hoạt động cụ thể đã đạt được thành công nhất cả từ triển khai dịch vụ và người tiêu dùng. Nó cho phép các hoạt động phát triển độc lập và rất rõ ràng về những gì được mong đợi bởi dịch vụ và những gì sẽ được trả lại cho khách hàng.

+1

Tôi đồng ý với khái niệm hiển thị DTO (đối tượng truyền dữ liệu) thay vì mô hình miền nội bộ của bạn. Điều này có nghĩa rằng DTO có thể quan tâm đến phản hồi bạn đang gửi thông qua dịch vụ và đối tượng miền có liên quan với đại diện cho thực thể nghiệp vụ. – Fenton

+0

@Sohnee Cảm ơn. Giả sử tôi có một thiết kế dựa trên OOAD, chúng ta có cần chuyển đổi nó thành DTO (như đề xuất ở trên) bằng cách sử dụng một lớp riêng biệt trước khi sử dụng trong dịch vụ không? Đó có phải là một thực hành tốt? Chúng ta có bất kỳ bài viết hay nào giải thích PROS và CONS của phương pháp này không? – Lijo

+0

@jageall. Cảm ơn lời giải thích rõ ràng. Ba câu hỏi - 1) AutoMapper có sản xuất "DTO hoạt động cụ thể" hay "DTO từ hành vi miền không có"? 2) Các công cụ mà bạn sử dụng cho phương pháp "Hoạt động cụ thể DTO" là gì? 3) Chúng tôi có bất kỳ bài viết/hướng dẫn nào giải thích việc sử dụng kho lưu trữ chung.Load ? – Lijo

1

Tôi nghĩ rằng việc thực hành tốt nhất ở đây là không sử dụng thừa kế trong dữ liệu hợp đồng của bạn:

[DataContract] 
class SavingsAccount 
{ 
    public string AccountNr { ... } 
    .... 
} 

[DataContract] 
class FixedAccount 
{ 
    public string AccountNr { ... } 
    .... 
} 

Những công trình wel đối với hầu hết các kịch bản nhưng không phải để có được một danh sách các tài khoản khác nhau.
Nếu điều đó là thực sự cần thiết, hãy xem xét:

[DataContract] 
class AccountDTO 
{ 
    public string AccountType { ... } 
    public string AccountNr { ... } 
    .... 
} 

trong đó chủ yếu là câu trả lời @ jageall của.

+0

Cảm ơn. Bạn có nghĩ rằng, một dịch vụ trả về một kết quả được trộn lẫn là một ý tưởng không tốt cho bạn? Thực hành tiêu chuẩn thông thường ở đây là gì? – Lijo

1

tôi sẽ đi khá nhiều với những gì người khác đã nói ở đây, nhưng có lẽ cần bổ sung sau đây:

  • Hầu hết các hệ thống SOA sử dụng Web Services để liên lạc. Các dịch vụ Web trưng ra giao diện của chúng thông qua WSDL. WSDL không có bất kỳ sự hiểu biết về thừa kế.
  • Tất cả hành vi trong DTOs của bạn sẽ được mất khi họ vượt qua dây
  • Tất cả tin/bảo vệ lĩnh vực sẽ bị mất khi họ đi qua dây

Hãy tưởng tượng kịch bản này (trường hợp là ngớ ngẩn nhưng minh họa):

public abstract class BankAccount 
{ 
    private DateTime _creationDate = DateTime.Now; 

    public DateTime CreationDate 
    { 
     get { return _creationDate; } 
     set { _creationDate = value; } 
    } 

    public virtual string CreationDateUniversal 
    { 
     get { return _creationDate.ToUniversalTime().ToString(); } 
    } 
} 

public class SavingAccount : BankAccount 
{ 
    public override string CreationDateUniversal 
    { 
     get 
     { 
      return base.CreationDateUniversal + " UTC"; 
     } 
    } 
} 

Và bây giờ bạn có u sed "Thêm tham chiếu dịch vụ" hoặc "Thêm tham chiếu web" trên máy khách của bạn (và không sử dụng lại các hội đồng) để truy cập tài khoản tiết kiệm.

SavingAccount account = serviceProxy.GetSavingAccountById(id); 
account.CreationDate = DateTime.Now; 
var creationDateUniversal = account.CreationDateUniversal; // out of sync!! 

gì sẽ xảy ra là thay đổi đối với CreationDate sẽ không được đáp lại với CreationDateUniversal vì không có thực hiện vượt qua dây, chỉ có giá trị của CreationDateUniversaltại thời điểm serialization tại máy chủ.

+0

Cảm ơn. Bạn có thể vui lòng xây dựng ví dụ ít hơn? Ý bạn là gì bởi "những thay đổi đối với CreationDate sẽ không được đáp lại". Liệu nó có nghĩa là tôi đã thay đổi logic trong lớp cơ sở? – Lijo

+1

@Lijo có nghĩa là khi trên ** server ** tôi thay đổi 'CreationDate', giá trị của' CreationDateUniversal' cũng thay đổi, vì nó sử dụng trường được bảo vệ để tính giá trị. Nhưng trên ** client **, các giá trị này đến như là thuộc tính auto đơn giản, vì vậy nếu bạn thay đổi 'CreationDate' thì' CreationDateUniversal' nó sẽ không bị thay đổi. – Aliostad

Các vấn đề liên quan