这是实现 ITaggable 功能的适当方法吗?

发布于 2024-12-12 02:21:07 字数 1206 浏览 0 评论 0原文

我正在考虑向我所承担的项目添加可标记功能。该项目是 3 层(mvc3 - 域 - 存储库)。

我需要添加在该系统中标记各种对象的功能。因为标签可以附加到许多不同的聚合根,所以我认为最好将标签作为自己的根(域中的rep/ITagManager)。

我的想法是拥有一个 ITaggable 接口,类似于:

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags);
    bool SaveTag(ITag _tag);
    IList<ITag> GetTags();
    bool HasTag(IList<ITag> _tags);
    bool HasTag(ITag _tag);
    bool HasTag(string _tagName);
}

我的想法是拥有一个 ITagManager,它具有获取 ITaggable 对象并保存/加载附加到它们的标签的方法(也许使用类似 String.Concat(typeof(this), this .ID)为实现ITaggable接口的对象生成唯一的ID)。 现在有两种可能的路线,首先将实现 ITaggable 接口的任何对象传递到 ITagManager 本身,或者将 Itaggable 接口修改为如下所示:

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags, ITagManager _tagManager);
    bool SaveTag(ITag _tag, ITagManager _tagManager);
    IList<ITag> GetTags(ITagManager _tagManager);
    bool HasTag(IList<ITag> _tags, ITagManager _tagManager);
    bool HasTag(ITag _tag, ITagManager _tagManager);
    bool HasTag(string _tagName, ITagManager _tagManager);
}

第一个解决方案可能有点贫血模型的味道,但第二个解决方案似乎很混乱。我知道可以注入依赖项,但我认为将其作为函数参数将使发生的事情变得显而易见。或者将其注入到对象中会更好吗?

这些解决方案是否合适?

任何帮助/建议将不胜感激。

I'm looking at adding taggable functionality to a project I've taken on. The project is 3 tier (mvc3 - Domain - Repositories).

I need to add the ability to tag various objects within this system. Because Tags can be attached to many different aggregate roots, I though it best to have tags as their own root (rep / ITagManager in domain).

My Idea was to have an ITaggable interface something similar to:

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags);
    bool SaveTag(ITag _tag);
    IList<ITag> GetTags();
    bool HasTag(IList<ITag> _tags);
    bool HasTag(ITag _tag);
    bool HasTag(string _tagName);
}

I had the idea to have an ITagManager which has methods to take ITaggable objects and save/load tags attached to them (perhaps using something like String.Concat(typeof(this), this.ID) to generate a unique ID for the object which implements the ITaggable interface).
Now there are two possible routes, first pass in any object that implements the ITaggable interface into the ITagManager itself, or modify the Itaggable interface to something like this:

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags, ITagManager _tagManager);
    bool SaveTag(ITag _tag, ITagManager _tagManager);
    IList<ITag> GetTags(ITagManager _tagManager);
    bool HasTag(IList<ITag> _tags, ITagManager _tagManager);
    bool HasTag(ITag _tag, ITagManager _tagManager);
    bool HasTag(string _tagName, ITagManager _tagManager);
}

The first solution perhaps smells of anemic model, but the second solution seems messy. I know that dependency could be injected, but I figured having it as a function parameter would make it obvious as to what was going on. Or would it be better to inject it into the object?

Are any of these solutions suitable?

Any help/advice would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

醉殇 2024-12-19 02:21:07

我认为您的“ITagable”界面不需要那么臃肿。另外,我不会将标签建模为聚合根,因为标签本身没有任何意义。

这是我们使用的接口:

public interface ITagable
{
    IEnumerable<Tag> Tags { get; }
    void Tag(Tag tag);
    void Untag(Tag tag);
}

如果需要,接口上的许多方法都可以轻松实现为扩展方法。

有时您无法处理域对象中的所有逻辑。这就是域服务有用的地方,也是我们用来处理“ITagable”实体上标签的“处理”的地方:

public interface ITagService
{
    void ProcessTags<TEntity>(TEntity entity, Func<IEnumerable<Tag>> featureTags, 
        string tagString) where TEntity : ITagable;
}

注意,我们传入了“featureTags”列表。在博客示例中,这将是整个博客的标签列表,因为我们不想创建重复的标签。因此,“TagService”不会进行任何类型的持久性,它只是在需要时创建新标签(在博客上),并在需要时在实体上标记或取消标记:

public class TagService : ITagService
{      
    public void ProcessTags<TEntity>(TEntity entity, 
        Func<IEnumerable<Tag>> featureTagsFactory, string tagString) where TEntity : ITagable
    {
        var result = new List<Tag>();
        // remove any leading/trailing spaces
        tagString = tagString.Trim();

        if (tagString.IsNotNullOrEmpty())
        {
            result.AddRange(from str in tagString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(t => t.Length > 1).Distinct()
                            join tag in featureTagsFactory() on (Slug)str equals tag.Slug into tags
                            from tag in tags.DefaultIfEmpty()
                            select tag ?? new Tag(str.Trim()));
        }

        // merge tags
        foreach (var tag in entity.Tags.Except(result)) // remove unmatched tags
        {
            entity.Untag(tag);
        }
        foreach (var tag in result) // entity should check if already added
        {
            entity.Tag(tag);
        }
    }
}

当我们更新可标记实体时(通常从我们的 UI 层传递一个逗号分隔的标签列表)我们执行以下操作:

// tags
if (command.TagString.IsNotNullOrEmpty())
{
    tagService.ProcessTags(post, () => blog.Tags, command.TagString);
}

在我的例子中,我们将所有标签映射到我们的父博客对象。您可能无法仅复制+粘贴此代码,但它应该让您了解如何处理实体上的标签处理。

I don't think your 'ITagable' interface needs to be quite as bloated. Also, I wouldn't model a Tag as an aggregate root as a tag doesn't make any sense on its own.

Here is the interface we use:

public interface ITagable
{
    IEnumerable<Tag> Tags { get; }
    void Tag(Tag tag);
    void Untag(Tag tag);
}

Many of the methods you have on your interface could easily be implemented as extension methods if you required.

Sometimes you can't handle all the logic within your domain objects. This is where domain services are useful and is what we use to handle the "processing" of tags on an 'ITagable' entity:

public interface ITagService
{
    void ProcessTags<TEntity>(TEntity entity, Func<IEnumerable<Tag>> featureTags, 
        string tagString) where TEntity : ITagable;
}

Notice we pass in the list of 'featureTags'. In a blog example, this would be the list of tags for the entire blog, since we don't want to create duplicate tags. The 'TagService' therefore, is not doing any kind of persistence, it is just creating new tags (on the blog) if it needs to, and tagging or untagging on the entity if it needs to:

public class TagService : ITagService
{      
    public void ProcessTags<TEntity>(TEntity entity, 
        Func<IEnumerable<Tag>> featureTagsFactory, string tagString) where TEntity : ITagable
    {
        var result = new List<Tag>();
        // remove any leading/trailing spaces
        tagString = tagString.Trim();

        if (tagString.IsNotNullOrEmpty())
        {
            result.AddRange(from str in tagString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(t => t.Length > 1).Distinct()
                            join tag in featureTagsFactory() on (Slug)str equals tag.Slug into tags
                            from tag in tags.DefaultIfEmpty()
                            select tag ?? new Tag(str.Trim()));
        }

        // merge tags
        foreach (var tag in entity.Tags.Except(result)) // remove unmatched tags
        {
            entity.Untag(tag);
        }
        foreach (var tag in result) // entity should check if already added
        {
            entity.Tag(tag);
        }
    }
}

When we update a tagable entity (normally passing a comma separated list of tags from our UI layer) we do the following:

// tags
if (command.TagString.IsNotNullOrEmpty())
{
    tagService.ProcessTags(post, () => blog.Tags, command.TagString);
}

In my case, we are mapping all tags to our parent blog object. You're probably not going to be able to just copy+paste this code but it should give you an idea of how you can handle the processing of tags on your entities.

阪姬 2024-12-19 02:21:07

为什么不为自己的有界上下文添加标签呢?我怀疑标签是否需要与相应聚合上的任何其他行为保持一致。

Why not make tagging its own bounded context? I doubt the tags need to be consistent with any other behavior on the respective aggregates.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文