我正在考虑为我所承担的项目添加可标记功能。该项目是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)来生成一个唯一的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);
}
第一种解决方案可能是贫血模型的气味,但第二种解决方案似乎很混乱。我知道可以注入依赖关系,但我认为将它作为一个函数参数可以明确地说明发生了什么。或者将它注入对象会更好吗?
这些解决方案中的任何一种都适用吗?
任何帮助/建议都将不胜感激。
答案 0 :(得分:5)
我不认为你的'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);
}
在我的例子中,我们将所有标签映射到我们的父博客对象。您可能无法复制+粘贴此代码,但它应该让您了解如何处理实体上的标记处理。
答案 1 :(得分:1)
为什么不标记自己的有界上下文?我怀疑标签需要与相应聚合上的任何其他行为保持一致。