Я не думаю, что ваш «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);
}
}
}
Когда мы обновляем тегируемую сущность (обычно передавая разделенный запятыми список тегов из нашего уровня пользовательского интерфейса), мы делаем следующее:
// tags
if (command.TagString.IsNotNullOrEmpty())
{
tagService.ProcessTags(post, () => blog.Tags, command.TagString);
}
В моем случае мы сопоставляем все теги с нашим родительским объектом блога.Вы, вероятно, не сможете просто скопировать + вставить этот код, но он должен дать вам представление о том, как вы можете обрабатывать теги в ваших сущностях.