这是实现ITaggable功能的合适方法吗?
本文关键字:方法 实现 ITaggable 功能 | 更新日期: 2023-09-27 18:12:44
我正在考虑为我所承担的项目添加可标记功能。该项目是3层的(mvc3 - Domain - Repositories)。
我需要在这个系统中添加标记各种对象的能力。因为标签可以附加到许多不同的聚合根,我认为最好有标签作为自己的根(rep/ITagManager in domain)。
我的想法是有一个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)来生成一个唯一的ID为对象实现ITaggable接口)。现在有两种可能的路径,首先将任何实现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);
}
第一种解决方案可能有贫血模型的味道,但第二种解决方案似乎很混乱。我知道依赖关系可以被注入,但我认为将它作为函数参数将使发生的事情变得明显。还是把它注入到对象中更好?
这些解决方案是否合适?
我不认为你的'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);
}
在我的例子中,我们将所有标签映射到父博客对象。您可能无法直接复制粘贴这些代码,但它应该让您了解如何处理实体上的标签。
为什么不让标签有自己的边界上下文呢?我怀疑标签是否需要与各自聚合上的任何其他行为保持一致。