Как управлять каким-то доменом "stati c data" в DDD - PullRequest
0 голосов
/ 01 мая 2020

Некоторое время я читаю о DDD и нахожу этот подход очень интересным.

Контекст

Я запускаю проект для создателя и менеджера таблицы персонажей Dungeon & Dragon, и у меня есть некоторые вопрос о реализации домена "stati c data".

Я знаю, DDD не о данных, но я не могу найти другое слово для моей проблемы.

Реализация

Я упрощаю следующий код, чтобы сосредоточиться на моих допросах. Например, я не писал нулевые проверки.

Персонаж

Мой персонаж - совокупность root, и мы можем выбрать его класс и 2 навыка из этого класса

public class Character : IAggregateRoot
{
    public Guid Id { get; }
    public string Name { get; private set; }
    public Class Class { get; private set; }

    ...

    // class is a keyword, so I use @class
    public void ChooseClass(Class @class)
    {
        Class = CharacterClass.Create(@class);
    }

    public void ChooseClassSkill(Skill skill)
    {
        Class.ChooseSkill(skill);
    }

    ...
}

Класс

Класс - это еще один агрегат root. В Dungeon & Dragon существует ограниченное количество классов, и их поведение отличается. Я хочу реализовать их в домене, чтобы представить это поведение. Некоторые классы могут иметь заклинания, некоторые могут носить тяжелую броню, ...

public abstract class Class : IAggregateRoot
{
    public string Id => GetType().Name;
    public abstract ICollection<Skill> AvailableSkills { get; }
}

Fighter

Класс Fighter является одним из реализованных классов. Я думал, что синглтон - хорошая идея для этого, потому что все персонажи, которые являются бойцами, имеют доступ к одним и тем же вещам. Специфика для одного символа находится в CharacterClass. После прочтения некоторых статей о том, что синглтон является анти-паттерном, я не уверен в этом.

public class Fighter : Class
{
    public override ICollection<Skill> AvailableSkills => new List<Skill>()
    {
        Skill.Acrobatics,
        Skill.Athletics,
        Skill.Perception
    };

    private static Fighter instance = null;

    internal static Fighter Instance
    {
        get
        {
            if (instance == null)
                instance = new Fighter();

            return instance;
        }
    }
}

Класс символов

Класс символов является сущностью и ссылается на агрегат класса root. Я читал статью о DDD, который сказал, что ссылка на другой агрегат root должна быть с его идентификатором.

public class CharacterClass : Entity
{
    public Guid Id { get; }
    public Class Class { get; }
    public ICollection<Skill> Skills { get; }

    public CharacterClass(Class @class)
    {
        Id = Guid.NewGuid();
        Class = @class;
        Skills = new List<Skill>();
    }

    public static CharacterClass Create(Class @class)
    {
        return new CharacterClass(@class);
    }

    public void ChooseSkill(Skill skill)
    {
        // These business rules can be check with this class properties
        if (Skills.Contains(skill))
            throw new Exception();
        if (Skills.Count > 1)
            throw new Exception();
        // This business rule need to check another Aggregate root
        if (!Class.AvailableSkills.Contains(skill);
            throw new Exception();

        Skills.Add(skill);
    }
}

Опрос

  • Должен ли синглтон быть хорошим или плохая идея для моих нужд?
  • Должен ли мой CharacterClass ссылаться на класс просто по id или по ссылке?
  • Если я ссылаюсь по id, как проверить, доступно ли умение для класса в функция ChooseSkill?

Заранее спасибо.

Редактировать

Я планирую повторно использовать сборку домена в приложении Blazor. Вот почему я хочу реализовать некоторые доменные «данные» и указать поведение c для классов. Мой бизнес логи c будет записан один раз.

1 Ответ

0 голосов
/ 01 мая 2020

ОК, я мог бы сделать это одним способом (с помощью Сервисов).

Я собираюсь переименовать несколько вещей и проигнорировать часть наследования - и выбрать другой подход.

  1. Переименование "класса" в SkillsClass
  2. Добавление AvailableSkillsFactory
  3. Замечание: навыки - это просто Enum
  4. Я собираюсь использовать другой Enum - CharacterType {1 = Боец}
// This is dummy code without input, such as a test - but could be in a Service etc.
SkillsClass availableSkills = AvailableSkillsFactory.Get(CharacterType.Fighter);

Character character = new Character(CharacterType.Fighter);
character.ChooseSkill(Skill.Perception, availableSkills);

Внутри класса персонажа я бы изменил ChooseSkill на этот ...

public void ChooseSkill(Skill skill, SkillsClass availableSkills)
{
    ...

    // This business rule need to check another Aggregate root
    bool isSkillAvailable = availableSkills.IsAvailable(skill);

    if (!isSkillAvailable) {
        // action
    }

    Skills.Add(skill);
}

Затем SkillsClass ...


public class SkillsClass {

  public IEnumerable<Skill> AvailableSkills { get; private set; }

  public SkillsClass (IEnumerable<Skill> skills) {
     this.AvailableSkills = skills;
  }

  public bool IsAvailable (Skill skill) {
    var doesExist = this.AvailableSkills.Contains(skill);

    return doesExist;
  }

}

И Фабрика ...

public static class AvailableSkillsFactory {

   public static Get(CharacterType characterType) {

      if (characterType == CharacterType.Fighter) {
         var availableSkills = new Fighter().AvailableSkills;
         return new SkillsClass(availableSkills);
      }

   }

}

Я ходил взад-вперед по нескольким именам и решил не слишком много реорганизовывать, чтобы было довольно просто.

Там Есть несколько способов сделать это.

...