Reputation: 3582
in my project ,the Business Logic all in the Application Service,Domain Service just some entity,who can tell me or give me a example to show How to Add Business Logic to the Domain Service in Domain-Driven-Design? very thanks!
UPDATE
i write a simple solutation,this solutation is a vote system,the solutation main part is:
Vote.Application.Service.VoteService.cs:
namespace Vote.Application.Service
{
public class VoteService
{
private IVoteRepository _voteRepository;
private IArticleRepository _articleRepository;
public VoteService(IVoteRepository voteRepository,IArticleRepository articleRepository)
{
_voteRepository = voteRepository;
_articleRepository = articleRepository;
}
public bool AddVote(int articleId, string ip)
{
var article = _articleRepository.Single(articleId);
if (article == null)
{
throw new Exception("this article not exist!");
}
else
{
article.VoteCount++;
}
if (IsRepeat(ip, articleId))
return false;
if (IsOvertakeTodayVoteCountLimit(ip))
return false;
_voteRepository.Add(new VoteRecord()
{
ArticleID = articleId,
IP = ip,
VoteTime = DateTime.Now
});
try
{
_voteRepository.UnitOfWork.Commit();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
private bool IsRepeat(string ip, int articleId)
{
//An IP per article up to cast 1 votes
//todo
return false;
}
private bool IsOvertakeTodayVoteCountLimit(string ip)
{
//An IP per day up to cast 10 votes
//todo
return false;
}
}
}
Vote.Domain.Contract.IVoteRepository.cs:
namespace Vote.Domain.Contract
{
public interface IVoteRepository
: IRepository<VoteRecord>
{
void Add(VoteRecord model);
}
}
Vote.Domain.Contract.IArticleRepository.cs:
namespace Vote.Domain.Contract
{
public interface IArticleRepository
: IRepository<Article>
{
void Add(VoteRecord model);
Article Single(int articleId);
}
}
Vote.Domain.Entities.VoteRecord:
namespace Vote.Domain.Entities
{
public class VoteRecord
{
public int ID { get; set; }
public DateTime VoteTime { get; set; }
public int ArticleID { get; set; }
public string IP { get; set; }
}
}
Vote.Domain.Entities.Article:
namespace Vote.Domain.Entities
{
public class Article
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int VoteCount { get; set; }
}
}
i want move the Business Login in application.service to Domain.service(current not this project),who can help me?how to do is reasonable? very thanks!
Upvotes: 6
Views: 4154
Reputation: 75316
DDD is focusing how to design domain models to fit with requirement, the schema in database does not matter much.
If your domain entity is just property bag, seems you are violating the Anemic Model anti-pattern. Business logic should be in domain entities. So, in your case, in order to avoid business logic leaking to Application service. You can have a new model called Client for example to store Ip or other properties if necessary.
To have easier understanding, whether Client exceeds the limits in day, this method should be in Client class. Similar with the method IsRepeated.
So, your domain object should be:
public class Client
{
public string Ip { get; set; }
// More properties if needed
public List<Vote> Votes { get; set; }
public bool ExceedLimitInDay()
{
}
}
public class Vote
{
public int Id { get; set; }
public DateTime VoteTime { get; set; }
public Article Article { get; set; }
public Client { get; set; }
}
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<Vote> Votes { get; set; }
public bool IsRepeated(string ip)
{
return Votes.Select(v => v.Client.Ip == ip).Any();
}
}
Note: If you don't need to create new table Client, just map it into Vote table. As for VoteCount properties, it is needless because you can count based on the list of Vote
Upvotes: 7
Reputation: 2954
I think Vote and Article are an Aggregate. I don't know much about your domain, but I assume each vote is only important to a given Article. Votes won't be shared across Articles so they should be treated as members of the aggregation, and not treated as entities of the whole domain.
In my opinion, Article would be the root entity of the Aggregate and Vote would be a member entity inside. Applying this "pattern" you could encapsulate all the business logic inside.
You could have a AddVote(...) inside your article, or some business logic like this.
Then you might be wondering.. "Ok.. but what about persistance??". Since you are defining your Article-Vote as an Aggregate, you then would treat them together. If you perform CRUD operations to an Article, at the same time you will be performing these operations in their Aggregate members (votes). So you might need a repository for the ArticleAggregate, it will take care of article's votes.
Hope this make sense to you. Choose the best option depending on your domain.
Upvotes: 2
Reputation: 2990
From glancing at the Application Service I'd say it's hardly an Application Service, it's already a bit of a Domain Service in the sense that it captures some domain logic (as opposed to taking viewmodel input and mapping it onto entities). You do however have bigger problems:
Your aggregates are off. You want to enforce two invariants around an IP address casting a vote on an article (an ip can only vote once per article, an ip can cast up to 10 votes per day). Neither nouns are a good fit (article, vote) for tracking this.
ArticleVotes might be a good fit to track and enforce votes cast on an article by individual ips, while IPVotesPerDate might track the number of votes cast by an individual IP address on a per day basis. You might be able to move ArticleVotes into the Article aggregate if the total number of votes is low enough (and you can keep performance where you want it at). IPVotesPerDate could be bent into VotesPerDate if the total number of IPs that votes on a day by day basis is low.
Bottom line is that the ip and voting volume are going to dictate the performance of your aggregates. This might force you to remodel them to accomodate the performance numbers you seek. Your best shot is to go find those numbers to know what direction to proceed in.
From there it should be obvious that the desired behavior could be brought into the aggregates I mentioned. Hence, the need for a domain service might go away. I say might, because the use case is still an ip casting a vote on an article. As such that may require some coordination across aggregates, ergo the introduction of a domain service.
Upvotes: 0