2009-03-04 29 views
20

Tôi đã đọc một số câu hỏi liên quan đến các mô hình miền thiếu máu và tách mối quan tâm. Các kỹ thuật tốt nhất để thực hiện/gắn logic miền trên các đối tượng miền thiếu máu là gì? Trong công việc của tôi, chúng tôi có một mô hình khá thiếu máu và chúng tôi hiện đang sử dụng các lớp "trợ giúp" để thực hiện cơ sở dữ liệu/logic nghiệp vụ trên các đối tượng miền. Ví dụ:Các kỹ thuật để xử lý mô hình miền thiếu máu

public class Customer 
{ 
    public string Name {get;set;} 
    public string Address {get;set;} 
} 

public class Product 
{ 
    public string Name {get;set;} 
    public decimal Price {get;set;} 
} 

public class StoreHelper 
{ 
    public void PurchaseProduct(Customer c, Product p) 
    { 
     // Lookup Customer and Product in db 
     // Create records for purchase 
     // etc. 
    } 
} 

Khi ứng dụng cần mua hàng, nó sẽ tạo StoreHelper và gọi phương thức trên đối tượng miền. Với tôi, nó sẽ có ý nghĩa đối với Khách hàng/Sản phẩm để biết cách lưu chính nó vào một kho lưu trữ, nhưng có thể bạn sẽ không muốn các phương thức Save() trên các đối tượng miền. Nó cũng có ý nghĩa đối với một phương thức như Customer.Purchase (Product), nhưng đó là đặt logic miền trên thực thể.

Dưới đây là một số kỹ thuật tôi đã đi qua, không chắc chắn đó là tốt/xấu:

  1. khách hàng và sản phẩm kế thừa từ một lớp "Entity", cung cấp các hoạt động CRUD cơ bản trong một thời trang generic (sử dụng ORM có thể).
    • Ưu điểm: Mỗi đối tượng dữ liệu sẽ tự động nhận được các thao tác CRUD, nhưng sau đó được gắn với cơ sở dữ liệu/ORM
    • Nhược điểm: Điều này không giải quyết vấn đề của hoạt động kinh doanh trên các đối tượng, và cũng có thể gắn tất cả các đối tượng miền đến một Entity cơ sở có thể không thích hợp
  2. lớp Sử dụng helper để xử lý các hoạt động CRUD và logic kinh doanh
    • Liệu nó làm cho tinh thần để có DAO cho "cơ sở dữ liệu thuần túy" hoạt động, và những người giúp đỡ kinh doanh riêng biệt cho họ hoạt động kinh doanh quặng cụ thể?
    • Tốt hơn là nên sử dụng các lớp trợ giúp tĩnh hoặc tĩnh không?
    • Ưu điểm: đối tượng miền không gắn với bất kỳ cơ sở dữ liệu/logic kinh doanh (hoàn toàn thiếu máu)
    • Nhược điểm: không phải là rất OO, không phải là rất tự nhiên để sử dụng người giúp đỡ trong mã ứng dụng (trông giống như mã C)
  3. sử dụng các kỹ thuật đúp Dispatch nơi đơn vị có phương pháp để lưu vào một kho lưu trữ tùy ý
    • Ưu điểm: tách tốt hơn các mối quan tâm
    • Nhược điểm: thực thể có thêm một số logic kèm theo (mặc dù nó tách riêng)
  4. Trong C# 3.0, bạn có thể sử dụng phương pháp khuyến nông để đính kèm các phương pháp CRUD/kinh doanh cho một đối tượng miền mà không cần chạm vào nó
    • Đây có phải là một cách tiếp cận hợp lệ? Ưu/nhược điểm là gì?
  5. Các kỹ thuật khác?

Kỹ thuật tốt nhất để xử lý vấn đề này là gì? Tôi khá mới với DDD (Tôi đang đọc cuốn sách của Evans - vì vậy có thể điều đó sẽ mở mắt của tôi)

Trả lời

7

Martin Fowler đã viết rất nhiều về các mô hình miền, bao gồm anemic domain models. Ông cũng có các mô tả ngắn gọn (và sơ đồ lớp UML) của nhiều mẫu thiết kế cho các mô hình miền và cơ sở dữ liệu có thể hữu ích: Catalog of "Patterns of Enterprise Application Architecture".

Tôi khuyên bạn nên xem các mẫu Active RecordData Mapper. Từ mô tả về sự cố của bạn, có vẻ như các lớp trợ giúp của bạn chứa cả chi tiết triển khai cơ sở dữ liệu về tên miền/doanh nghiệp .

Bản ghi hoạt động sẽ di chuyển tên miền của trình trợ giúp và mã cơ sở dữ liệu vào các đối tượng miền khác (như lớp cơ sở Entity) của bạn. Trình ánh xạ dữ liệu sẽ di chuyển logic miền của trình trợ giúp vào các đối tượng miền và mã cơ sở dữ liệu thành một đối tượng bản đồ riêng biệt. Hoặc là cách tiếp cận sẽ hướng đối tượng hơn các lớp trợ giúp theo kiểu thủ tục.

Cuốn sách "Thiết kế Driven miền" của Eric Evans thật xuất sắc. Nó hơi khô một chút, nhưng chắc chắn đáng giá. InfoQ có một số "Domain Driven Design Quickly" mini-book là một giới thiệu tốt về sách của Evans. Plus "Domain Driven Design Quickly" có sẵn dưới dạng PDF miễn phí.

2

Tôi luôn nghĩ về mô hình miền thiếu máu như một mẫu chống.Rõ ràng là một khách hàng sẽ mua sản phẩm, khả năng có thể được generised bởi một thực hiện giao diện

Interface IPurchase 
     Purchase(Product); 

, do đó, một bất kỳ của các đối tượng tên miền của bạn sau đó có thể thực hiện điều đó theo yêu cầu. Bằng cách đó, bạn có thể giới thiệu chức năng cho các đối tượng miền của mình - đó chính là vị trí của nó.

14

Để tránh mô hình bị thiếu máu, cấu trúc lại các lớp helper của bạn:

logic như:
"Customer.PurchaseProduct (sản phẩm sản phẩm, thanh toán thanh toán)",
"Customer.KillCustomer (Person kẻ giết người, vũ khí vũ khí) "
phải tồn tại ngay trong đối tượng miền" Khách hàng ".

logic như:
"Customer.IsCustomerAlive()"
"Customer.IsCustomerHappy()"
nên đến chi tiết kỹ thuật.

logic như:
"Customer.Create()",
"Customer.Update()"
rõ ràng nên đi đến kho.

logic như:
"Customer.SerializeInXml()"
"Customer.GetSerializedCustomerSizeInBytes()"
nên đến dịch vụ.

Nhà thầu phức tạp nên đến nhà máy.

Đó là cách tôi thấy. Tôi sẽ rất vui nếu có ai đó có thể nhận xét sự hiểu biết của tôi về cách tiếp cận DDD.


Edit:

Đôi - mô hình miền thiếu máu shouldn't be avoided.

Đã chỉnh sửa câu trả lời của tôi để thêm rằng DDD không phải là về chọn và bỏ mẫu.
DDD là cách chúng ta nghĩ.

+0

Dường như rất nhiều lớp khác nhau chỉ để xử lý khách hàng. Tại sao không ném hầu hết trong một lớp duy nhất, với một dịch vụ để xử lý bất cứ điều gì phức tạp? –

+0

Câu trả lời của tôi là cũ như địa ngục. : D –

+0

@LuckyLindy Chủ yếu là do DDD sắp tạo cầu nối giữa các chuyên gia miền và lập trình viên. Mô hình miền không được chứa nội dung kỹ thuật, nếu không ngôn ngữ phổ biến sẽ không thể tồn tại. Để di chuyển các công cụ kỹ thuật - chúng ta phải trừu tượng nó. Tóm tắt một cái gì đó luôn luôn thổi phồng cơ sở mã. –

0

Một phương pháp mà bạn chưa đề cập là sử dụng AOP để xử lý quyền truy cập dữ liệu của bạn. Một ví dụ về cách sử dụng gần đây của tôi về cách tiếp cận này (mặc dù rất đơn giản cho mục đích đăng bài) là tôi có một thực thể tên miền Account có phương thức debit, đóng gói logic nghiệp vụ cần thiết để thực hiện ghi nợ thành công từ tài khoản.

N.B. Tất cả các mã là Java với ký hiệu AspectJ AOP ...

public boolean debit(int amount) { 
    if (balance - amount >= 0) { 
     balance = balance - amount; 
     return true; 
    } 
    return false; 
} 

Với kho lưu trữ thích hợp tiêm vào khía cạnh, tôi sau đó sử dụng một pointcut để đánh chặn các cuộc gọi đến phương pháp này ...

pointcut debit(Account account,int amount) : 
    execution(boolean Account.debit(int)) && 
    args(amount) && 
    target(account); 

. ..và áp dụng một số lời khuyên:

after(Account account, int amount) returning (boolean result) : debit(account,amount) { 
    if (result) getAccountRepository().debit(account, amount); 
} 

Theo tôi, điều này cho phép chia tách mối quan tâm và cho phép thực thể tập trung hoàn toàn vào logic nghiệp vụ của y ứng dụng của chúng tôi.

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