Reputation: 12183
Consider the following scenario:
public class Document
{
private ISet<User> sharedWith;
public Document(string name)
{
this.sharedWith = new HashSet<User>();
this.Name = name;
}
public string Name { get; private set; }
public IEnumerable<User> SharedWith
{
get
{
return this.sharedWith;
}
}
public void ShareWith(User user)
{
if (this.SharedWith.Contains(user))
{
throw new Exception("This document is already shared with that user.");
}
this.sharedWith.Add(user);
}
}
Documents
can be shared with User
Obviously this does not scale very well, because of the need to check the SharedWith
for user existence, resulting in the ORM lazy-loading the entire collection into memory. I could do the existence check in the application service, but I consider this domain logic, and so it makes the most sense to me that I keep it in the Document class.
I can't seem to figure out how this should be done with DDD? And what if I am unable to use an ORM, how does one do this sort of stuff?
I suppose I should have a Document Aggregate and a User Aggregate?
I've looked at various DDD resources (I have not read the book though), but I can't seem to find an answer to this particular scenario.
Upvotes: 1
Views: 305
Reputation: 2473
this was quickly done up so it's not perfect but you get the gist of it:
public class User { public Guid UserId { get; set; } }
public class Document
{
public string Name { get; private set; }
private ICollection<User> sharedWith = new List<User>();
private DateTime? publishedOn;
public Document(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name is required");
}
this.Name = name;
}
public void Publish()
{
if (this.publishedOn.HasValue == false)
{
this.publishedOn = DateTime.UtcNow;
}
}
public void SharedWith(User user)
{
if (this.publishedOn.HasValue == false)
{
throw new InvalidOperationException("Document must be published for sharing is allowed.");
}
sharedWith.Add(user);
}
}
public interface IDocumentRepository
{
Document documentOfId(Guid id);
bool IsAlreadySharedWith(Guid documentId, Guid userId);
}
public interface IUseRepository
{
User userOfId(Guid id);
}
public class ShareDocumentService
{
private readonly IUseRepository userRepository;
private readonly IDocumentRepository documentRepository;
public void ShareWith(Guid userId, Guid documentId)
{
if (documentRepository.IsAlreadySharedWith(documentId, userId))
throw new InvalidOperationException("Document has already been shared with user.");
User user = userRepository.userOfId(userId);
Document doc = documentRepository.documentOfId(documentId);
doc.SharedWith(user);
}
}
Upvotes: 2
Reputation: 7158
I suppose if you were modelling this in a paper/actor based world, then someone would have the job of marshalling who has access to which document(s) and this would probably rely on some sort of paper based artefact. To gain access to a document you would have to fill out a Document Request Form, which might go through an approval process.
This form in a paper based world, would become the many-to-many linking entity that becomes the key to user's accessing secure documents. It would make User
, Document
and DocumentRequestForm
three separate entities.
Upvotes: 1