如何在域驱动设计中将业务逻辑添加到域服务?

时间:2012-08-16 05:21:47

标签: c# architecture domain-driven-design

在我的项目中,业务逻辑全部在应用服务中,域服务只是一些实体,谁能告诉我或给我一个例子来展示如何在域驱动设计中向域服务添加业务逻辑?非常感谢!

更新

我写了一个简单的解释,这个解决方案是一个投票系统,解决主要部分是:

enter image description here

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; }
    }
}

我想将application.service中的Business Login移动到Domain.service(当前不是这个项目),谁能帮帮我?怎么做才合理?非常感谢!

3 个答案:

答案 0 :(得分:7)

DDD正在关注如何设计领域模型以满足需求,数据库中的模式并不重要。

如果您的域名实体只是属性包,那么您似乎违反了Anemic Model anti-pattern。业务逻辑应该在域实体中。因此,在您的情况下,为了避免业务逻辑泄漏到应用程序服务。例如,您可以拥有一个名为Client的新模型,以便在必要时存储Ip或其他属性。

为了更容易理解,客户端是否超过了白天的限制,此方法应该在Client类中。与IsRepeated方法类似。

因此,您的域对象应为:

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();      
    }
} 

注意:如果您不需要创建新的表Client,只需将其映射到Vote表。至于VoteCount属性,它是不必要的,因为你可以根据投票列表计算

答案 1 :(得分:1)

我认为投票和文章是一个聚合。 我对你的域名了解不多,但我认为每一票都只对给定的文章很重要。投票不会在文章中共享,因此它们应被视为聚合的成员,而不被视为整个域的实体。

在我看来,Article将是Aggregate的根实体,而Vote将是其中的成员实体。 应用此“模式”,您可以封装所有业务逻辑。

您可以在文章中添加一个AddVote(...),或者像这样的一些业务逻辑。

然后你可能会想..“好吧......但是持久性怎么样?” 由于您将Article-Vote定义为Aggregate,因此您可以将它们一起处理。如果您对文章执行CRUD操作,同时您将在其聚合成员(投票)中执行这些操作。因此,您可能需要ArticleAggregate的存储库,它将负责文章的投票。

希望这对你有意义。根据您的域名选择最佳选项。

答案 2 :(得分:0)

从浏览应用服务我会说它几乎不是一个应用服务,它已经是一个域服务,因为它捕获了一些域逻辑(与获取视图模型输入并将其映射到实体相反)。但是你有更大的问题:

您的汇总已关闭。你想在IP地址周围强制执行两个不变量,对一篇文章进行投票(每篇文章只能投票一次,每天最多可以投票10票)。这些名词都不适合(文章,投票)跟踪这个。

ArticleVotes可能非常适合跟踪和执行单个ips对文章投票的投票,而IPVotesPerDate可能会跟踪每天基于个人IP地址投票的数量。如果总票数足够低,您可以将ArticleVotes移动到Article聚合中(并且您可以将性能保持在您想要的位置)。如果按日投票的IP总数较低,则IPVotesPerDate可以弯曲成VotesPerDate。

底线是ip和投票量将决定聚合的性能。这可能会迫使您重新构建它们以适应您寻求的性能数字。你最好的方法就是去找那些数字来了解继续前进的方向。

从那里可以明显看出,所期望的行为可以被带入我提到的聚合中。因此,对域服务的需求可能会消失。我说可能,因为用例仍然是一个ip投票的文章。因此,可能需要跨聚合进行一些协调,因此引入了域服务。