Tôi không coi mình là một chuyên gia DDD nhưng, với tư cách là một kiến trúc sư giải pháp, hãy cố gắng áp dụng các thực tiễn tốt nhất bất cứ khi nào có thể. Tôi biết có rất nhiều cuộc thảo luận xung quanh "phong cách" chuyên nghiệp và không có (công khai) trong DDD và tôi có thể thấy cả hai mặt của tranh luận. Vấn đề của tôi là tôi làm việc trong một nhóm có sự đa dạng về kỹ năng, kiến thức và kinh nghiệm có nghĩa là tôi không thể tin rằng mọi nhà phát triển sẽ làm mọi thứ theo cách "đúng". Chẳng hạn, nếu các đối tượng miền của chúng ta được thiết kế sao cho việc thay đổi trạng thái bên trong của đối tượng được thực hiện bằng một phương thức nhưng cung cấp setters thuộc tính công cộng, chắc chắn sẽ có người đặt thuộc tính thay vì gọi phương thức. Sử dụng ví dụ này:
public class MyClass
{
public Boolean IsPublished
{
get { return PublishDate != null; }
}
public DateTime? PublishDate { get; set; }
public void Publish()
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
PublishDate = DateTime.Today;
Raise(new PublishedEvent());
}
}
Giải pháp của tôi là làm cho các setters thuộc tính riêng tư, điều này là có thể bởi vì ORM mà chúng ta đang sử dụng để hydrat hóa các đối tượng sử dụng sự phản chiếu để nó có thể truy cập vào các setters riêng. Tuy nhiên, điều này trình bày một vấn đề khi cố gắng viết bài kiểm tra đơn vị. Ví dụ, khi tôi muốn viết một bài kiểm tra đơn vị xác minh yêu cầu mà chúng tôi không thể xuất bản lại, tôi cần chỉ ra rằng đối tượng đã được xuất bản. Tôi chắc chắn có thể làm điều này bằng cách gọi Xuất bản hai lần, nhưng sau đó thử nghiệm của tôi cho rằng Xuất bản được triển khai chính xác cho cuộc gọi đầu tiên. Điều đó có vẻ hơi mùi.
Hãy tạo kịch bản thực tế hơn một chút với mã sau:
public class Document
{
public Document(String title)
{
if (String.IsNullOrWhiteSpace(title))
throw new ArgumentException("title");
Title = title;
}
public String ApprovedBy { get; private set; }
public DateTime? ApprovedOn { get; private set; }
public Boolean IsApproved { get; private set; }
public Boolean IsPublished { get; private set; }
public String PublishedBy { get; private set; }
public DateTime? PublishedOn { get; private set; }
public String Title { get; private set; }
public void Approve(String by)
{
if (IsApproved)
throw new InvalidOperationException("Already approved.");
ApprovedBy = by;
ApprovedOn = DateTime.Today;
IsApproved = true;
Raise(new ApprovedEvent(Title));
}
public void Publish(String by)
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
if (!IsApproved)
throw new InvalidOperationException("Cannot publish until approved.");
PublishedBy = by;
PublishedOn = DateTime.Today;
IsPublished = true;
Raise(new PublishedEvent(Title));
}
}
Tôi muốn viết bài kiểm tra đơn vị xác minh:
- Tôi không thể xuất bản trừ khi Tài liệu đã được phê duyệt
- Tôi không thể xuất bản lại Tài liệu
- Khi được xuất bản, các giá trị PublishBy và PublishOn được đặt đúng
- Khi xuất hiện, PublishEvent được nâng lên
Không có quyền truy cập vào setters, tôi không thể đặt đối tượng vào trạng thái cần thiết để thực hiện các bài kiểm tra. Mở truy cập vào setters đánh bại mục đích ngăn chặn truy cập.
Làm thế nào để (có) bạn giải quyết (d) vấn đề này?