Đây chủ yếu là mối quan tâm về kiến trúc và có thể phụ thuộc vào sở thích cá nhân một chút. Tôi sẽ cố gắng kiểm tra những ưu điểm và nhược điểm (thực sự chỉ có nhược điểm, điều này khá là có ý kiến):
Ở cấp độ cơ sở dữ liệu, MongoDB không cung cấp công cụ để thực thi tính toàn vẹn tham chiếu, vì vậy có, bạn phải tự làm điều này. Tôi khuyên bạn nên sử dụng các đối tượng cơ sở dữ liệu trông giống như sau:
public class DBObject
{
public ObjectId Id {get;set;}
}
public class Department : DBObject
{
// ...
}
public class EmployeeDB : DBObject
{
public ObjectId DepartmentId {get;set;}
}
Tôi khuyên bạn nên sử dụng DTO thuần túy như thế này ở cấp cơ sở dữ liệu. Nếu bạn muốn thêm đường, đặt nó trong một lớp riêng biệt ngay cả khi đó có nghĩa là một chút sao chép. Logic trong các đối tượng DB đòi hỏi một sự hiểu biết rất tốt về cách trình điều khiển hydrate đối tượng và có thể yêu cầu phải dựa vào chi tiết thực hiện.
Bây giờ, bạn có muốn làm việc với các đối tượng 'thông minh' hơn không. Thật vậy, nhiều người thích sử dụng các trình truy cập tự động kích hoạt mạnh mẽ, ví dụ:
public class Employee
{
public Department
{ get { return /* the department object, magically, from the DB */ } }
}
mẫu này đi kèm với một số thách thức:
- Nó đòi hỏi lớp
Employee
, một lớp mô hình, để có thể làm ẩm các đối tượng từ cơ sở dữ liệu. Đó là khó khăn, bởi vì nó cần phải có DB tiêm hoặc bạn cần một đối tượng tĩnh để truy cập cơ sở dữ liệu mà cũng có thể được khôn lanh.
- Truy cập
Department
trông hoàn toàn rẻ, nhưng trên thực tế, nó kích hoạt hoạt động cơ sở dữ liệu, nó có thể chậm, có thể không thành công. Điều này hoàn toàn ẩn khỏi người gọi.
- Trong mối quan hệ 1: n, mọi thứ trở nên phức tạp hơn rất nhiều. Ví dụ:
Department
cũng có hiển thị danh sách Employees
không? Nếu có, danh sách đó có thực sự là danh sách (tức là khi bạn bắt đầu đọc lần đầu tiên, tất cả nhân viên phải được deserialized không?) Hoặc có phải là số MongoCursor
lười biếng không?
- Để làm cho vấn đề tồi tệ hơn, thường không rõ loại bộ nhớ đệm nào nên được sử dụng. Giả sử bạn nhận được
myDepartment.Employee[0].Department.Name
. Rõ ràng, mã này không phải là thông minh, nhưng hãy tưởng tượng có một ngăn xếp cuộc gọi với một vài phương pháp chuyên ngành. Họ có thể gọi mã giống như vậy, ngay cả khi nó ẩn hơn. Bây giờ một triển khai ngây thơ sẽ thực sự de-serialize ref'd Department
một lần nữa. Đó là xấu xí. Mặt khác, bộ nhớ đệm tích cực là nguy hiểm bởi vì bạn thực sự có thể muốn để tìm nạp lại đối tượng.
- Điều tồi tệ nhất: Cập nhật. Cho đến nay, những thách thức phần lớn là chỉ đọc. Bây giờ, giả sử tôi gọi
employeeJohn.Department.Name = 'PixelPushers'
và employeeJohn.Save()
. Điều đó có cập nhật Bộ hay không? Nếu có, các thay đổi đối với john được tuần tự hóa lần đầu tiên hay sau khi thay đổi đối tượng phụ thuộc? Điều gì về versioning và khóa?
- Nhiều ngữ nghĩa khó thực hiện:
employeJohn.Department.Employees.Clear()
có thể phức tạp.
Nhiều ORM sử dụng một tập hợp các mẫu phức tạp để cho phép các hoạt động này, do đó, những vấn đề này không thể làm việc xung quanh. Nhưng ORM thường nằm trong khoảng 100k đến hơn 1M dòng mã (!), Và tôi nghi ngờ bạn có loại thời gian đó. Trong một RDBMS, cần phải kích hoạt các đối tượng liên quan và sử dụng sth. như ORM nghiêm trọng hơn nhiều, bởi vì bạn không thể nhúng ví dụ danh sách các chi tiết đơn hàng trong hóa đơn, vì vậy, mỗi mối quan hệ 1: n hoặc m: n phải được thể hiện bằng cách sử dụng một kết nối. Đó được gọi là sự không phù hợp với đối tượng.
Ý tưởng về cơ sở dữ liệu tài liệu, như tôi đã hiểu, là bạn không cần phải phá vỡ mô hình của mình một cách không tự nhiên như bạn có trong RDBMS. Tuy nhiên, có 'biên giới đối tượng'. Nếu bạn nghĩ về mô hình dữ liệu của mình như một mạng lưới các nút được kết nối, thì thách thức là phải biết phần nào của dữ liệu bạn hiện đang làm việc.
Cá nhân, tôi không muốn đặt một lớp trừu tượng lên trên, vì trừu tượng đó bị rò rỉ, nó giấu những gì thực sự xảy ra với người gọi và cố gắng giải quyết mọi vấn đề với cùng một cái búa.
Một phần của ý tưởng NoSQL là các mẫu truy vấn của bạn phải được kết hợp cẩn thận với mô hình dữ liệu, vì bạn không thể áp dụng búa JOIN cho bất kỳ bảng nào trong tầm nhìn.
Vì vậy, ý kiến của tôi là: dính vào một lớp mỏng và thực hiện hầu hết hoạt động cơ sở dữ liệu trong một lớp dịch vụ. Di chuyển DTO xung quanh thay vì thiết kế một mô hình miền phức tạp tách rời ngay khi bạn cần thêm khóa, mvcc, cập nhật xếp chồng, v.v.