2009-10-22 3 views
2

я в настоящее время есть классы обслуживания, которые выглядят что-то вроде этогоВход на дизайн класса

public class UserService : IUserService 
{ 
    private IAssignmentService _assignmentService; 
    private ILocationService _locationService; 
    private IUserDal _userDal; 
    private IValidationDictionary _validationDictionary; 
    public UserService(IAssignmentService assignmentService, ILocationService locationService, IValidationDictionary validationDictionary) 
    { 
     this._assignmentService = assignmentService; 
     this._locationService = locationService; 
     this._userDAL = new UserDal(); 
     this._validationDictionary = validationDictionary; 
    } 

    ..... 

    private void ValidateUser(IUser user) 
    { 
     if (_locationService.GetBy(user.Location.Id) == null) 
      _validationDictionary.AddError("...."); 
     if (_assignmentService.GetBy(user.Assignment.Id) == null) 
      _validationDictionary.AddError("...."); 
     ..... 
    } 
} 

И DAL классов, который выглядит, как этот

public class UserDal: IUserDal 
{ 
    private IAssignmentDal _assignmentDal; 
    private ILocationDAL _locationDAL 

    public UserDal() 
    { 
     this_assignmentDal = new AssignmentDal(); 
     this._locationDal = new LocationDal(); 
    } 

    public int AddUser(IUser user) 
    { 
     // db call and insert user 
     _locationDal.Add(user.Location); 
     _assignmentDal.Add(user.Assignment); 
    } 

    public IUser GetUser(int id) 
    { 
     ..DB Call 

     IUser user = new User() { userData, GetLocation(dr["Location_Id"]),GetAssignment([dr["Assignment_Id"]); 
     return user 
    } 

    private ILocation GetLocation(int id) 
    { 
     return new LocationDal().GetById(id); 
    } 
    private IAssignment GetAssignment(int id) 
    { 
     return new AssignmentDal().GetById(id); 
    } 
} 

мне было интересно, если это считается плохой дизайн, чтобы иметь Слой службы разговаривает с другими объектами уровня обслуживания и говорит Dal с другими объектами Dal?

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

ответ

5

Учитывая дизайн ваших примеров, вы столкнетесь с тем, что мне нравится называть адским ад. Это, безусловно, возможность спуститься по маршруту, который вы принимаете, но это приведет к высокоразвитой архитектуре, которая, вероятно, будет очень трудно поддерживать и реорганизовать. Тем не менее, если вы немного абстрагируетесь, вы можете упростить свою архитектуру, организовать обязанности немного больше и разделить проблемы таким образом, чтобы управлять вашими зависимостями было намного проще.

Службы UserService, AssignmentService и LocationService представляют собой службы стиля CRUD. Более подходящим термином для них будет Entity Services. Служба сущности должна нести исключительную ответственность за операции CRUD ближайшего объекта, и ничего больше. Операции, которые связаны с несколькими сущностями, отношениями между сущностями и т. Д., Могут быть перенесены на службу более высокого уровня, которая может организовать более масштабные операции. Их часто называют Orchestration или Task Services.

Я бы рекомендовал такой подход, как следующее. Цели здесь состоят в том, чтобы упростить каждую службу, чтобы предоставить ей наименьшую область ответственности и контролировать зависимости. Упрощение контрактов на обслуживание, чтобы уменьшить ответственность существующих услуг сущностей, и добавить две новые услуги:

// User Management Orchestration Service 
interface IUserManagementService 
{ 
    User CreateUser(); 
} 

// User Entity Service 
interface IUserService 
{ 
    User GetByKey(int key); 
    User Insert(User user); 
    User Update(User user); 
    void Delete(User user); 
} 

// User Association Service 
interface IUserAssociationService 
{ 
    Association FindByUser(User user); 
    Location FindByUser(User user); 
    void AssociateWithLocation(User user, Location location); 
    void AssociateWithAssignment(User user, Assignment assignment); 
} 

// Assignment Entity Service 
interface IAssignmentService 
{ 
    Assignment GetByKey(int key); 
    // ... other CRUD operations ... 
} 

// Location Entity Service 
interface ILocationService 
{ 
    Location GetByKey(int key); 
    // ... other CRUD operations ... 
} 

Процесс создания пользователя и связать его с места и назначения будет принадлежать UserManagementService, который сочинял услуги сущности более низкого уровня:

class UserManagementService: IUserManagementService 
{ 
    public UserManagementService(IUserService userService, IUserAssociationService userAssociationService, IAssignmentService assignmentService, ILocationService locationService) 
    { 
     m_userService = userService; 
     m_userAssociationService = userAssociationService; 
     m_assignmentService = assignmentService; 
     m_locationService = locationService; 
    } 

    IUserService m_userService; 
    IUserAssociationService m_userAssociationService; 
    IAssignmentService m_assignmentService; 
    ILocationService m_locationService; 

    User CreateUser(string name, {other user data}, assignmentID, {assignment data}, locationID, {location data}) 
    { 
     User user = null; 
     using (TransactionScope transaction = new TransactionScope()) 
     { 
      var assignment = m_assignmentService.GetByKey(assignmentID); 
      if (assignment == null) 
      { 
       assignment = new Assignment { // ... }; 
       assignment = m_assignmentService.Insert(assignment); 
      } 

      var location = m_locationService.GetByKey(locationID); 
      if (location == null) 
      { 
       location = new Location { // ... }; 
       location = m_locationService.Insert(location); 
      } 

      user = new User 
      { 
       Name = name, 
       // ... 
      }; 
      user = m_userService.Insert(user); 
      m_userAssociationService.AssociateWithAssignment(user, assignment); 
      m_userAssociationService.AssociateWithLocation(user, location); 
     } 

     return user; 
    } 
} 

class UserService: IUserService 
{ 
    public UserService(IUserDal userDal) 
    { 
     m_userDal = userDal; 
    } 

    IUserDal m_userDal; 

    public User GetByKey(int id) 
    { 
     if (id < 1) throw new ArgumentException("The User ID is invalid."); 

     User user = null; 
     using (var reader = m_userDal.GetByID(id)) 
     { 
      if (reader.Read()) 
      { 
       user = new User 
       { 
        UserID = reader.GetInt32(reader.GerOrdinal("id")), 
        Name = reader.GetString(reader.GetOrdinal("name")), 
        // ... 
       } 
      } 
     } 

     return user; 
    } 

    public User Insert(User user) 
    { 
     if (user == null) throw new ArgumentNullException("user"); 
     user.ID = m_userDal.AddUser(user); 
     return user; 
    } 

    public User Update(User user) 
    { 
     if (user == null) throw new ArgumentNullException("user"); 
     m_userDal.Update(user); 
     return user; 
    } 

    public void Delete(User user) 
    { 
     if (user == null) throw new ArgumentNullException("user"); 
     m_userDal.Delete(user); 
    } 
} 

class UserAssociationService: IUserAssociationService 
{ 
    public UserAssociationService(IUserDal userDal, IAssignmentDal assignmentDal, ILocationDal locationDal) 
    { 
     m_userDal = userDal; 
     m_assignmentDal = assignmentDal; 
     m_locationDal = locationDal; 
    } 

    IUserDal m_userDal; 
    IAssignmentDal m_assignmentDal; 
    ILocationDal m_locationDal; 

    public Association FindByUser(User user) 
    { 
     if (user == null) throw new ArgumentNullException("user"); 
     if (user.ID < 1) throw new ArgumentException("The user ID is invalid."); 

     Assignment assignment = null; 
     using (var reader = m_assignmentDal.GetByUserID(user.ID)) 
     { 
      if (reader.Read()) 
      { 
       assignment = new Assignment 
       { 
        ID = reader.GetInt32(reader.GetOrdinal("AssignmentID")), 
        // ... 
       }; 

       return assignment; 
      } 
     } 
    } 
} 

class UserDal: IUserDal 
{ 
    public UserDal(DbConnection connection) 
    { 
     m_connection = connection; 
    } 

    DbConnection m_connection; 

    public User GetByKey(int id) 
    { 
     using (DbCommand command = connection.CreateCommand()) 
     { 
      command.CommandText = "SELECT * FROM Users WHERE UserID = @UserID"; 
      var param = command.Parameters.Add("@UserID", DbType.Int32); 
      param.Value = id; 

      var reader = command.ExecuteReader(CommandBehavior.SingleResult|CommandBehavior.SingleRow|CommandBehavior.CloseConnection); 
      return reader;     
     } 
    } 

    // ... 
} 

class AssignmentDal: IAssignmentDal 
{ 

    public AssignmentDal(DbConnection connection) 
    { 
     m_connection = connection; 
    } 

    DbConnection m_connection; 

    Assignment GetByUserID(int userID) 
    { 
     using (DbCommand command = connection.CreateCommand()) 
     { 
      command.CommandText = "SELECT a.* FROM Assignments a JOIN Users u ON a.AssignmentID = u.AssignmentID WHERE u.UserID = @UserID"; 
      var param = command.Parameters.Add("@UserID", DbType.Int32); 
      param.Value = id; 

      var reader = command.ExecuteReader(CommandBehavior.SingleResult|CommandBehavior.SingleRow|CommandBehavior.CloseConnection); 
      return reader;     
     } 
    } 

    // ... 
} 

// Implement other CRUD services similarly 

концептуальные слои и поток данных/объектов, которые являются результатом этой архитектуры будет выглядеть следующим образом:

Task:       UserManagementSvc 
            ^
             | 
      ------------------------------------------------- 
      |    |     |    | 
Entity: UserSvc UserAssociationsSvc AssignmentSvc LocationSvc 
      ^ ^     ^   ^
      |  |      |    | 
      ---------      -    - 
       |       |    | 
Utility:  UserDal     AssignmentDal LocationDal 
       ^      ^   ^
       |       |    | 
       --------------------------------------------- 
             | 
DB:        (SQL Database) 

Несколько ключевых вещей, которые нужно отметить здесь относительно композиции и зависимостей.Добавляя UserManagementService и составляя в нем услуги сущности, вы достигаете следующих целей:

  1. Устранение связи между службами сущностей.
  2. Сокращение объема зависимостей для каждой службы сущностей.
    • Они зависят только от их DAL и, возможно, от общей инфраструктуры.
  3. Зависимости теперь однонаправлены: все зависимости «нисходящие», никогда не «горизонтальные» или «вверх».
    • Это простое правило обеспечивает очень чистый механизм, благодаря которому беспорядочные зависимости могут быть полностью устранены.
  4. Правила связывания пользователя с назначением и местоположением удаляются из сущностей и выталкиваются выше.
    • Это обеспечивает более гибкие композиции и поощряет повторное использование кода.
    • Другие сервисы, такие как UserManagementService, могут быть написаны, которые составляют User, Assignment и Location и/или другие объекты по-разному, чтобы соответствовать различным бизнес-правилам и решать различные проблемы.
  5. Даже услуги более высокого уровня могут быть написаны выше UserManagementService и аналогичных сервисов, составив их аналогичным образом, создавая даже более высокие рабочие процессы с минимальными усилиями.

Если вы внимательно относитесь к тому, как вы разрабатываете и пишете каждый уровень услуг, вы можете обеспечить множество уровней различной ответственности, сложности и способности к сшиванию. Приложение становится меньше о написании бизнес-правил и о создании частей для создания бизнес-поведения. Если часть должна быть написана, ее обычно можно записать, составив другие части и, возможно, добавив небольшое количество дополнительного поведения. Построение приложения становится намного проще, и гораздо проще создавать полностью функциональные, автономные, многоразовые части, которые легче тестировать изолированно и проще развертывать.

Вы также достигли Service-Orientation в самом прямом смысле (в соответствии с Thomas Erl в любом случае), а также все его преимущества. ;)

+0

Спасибо, что это начинает иметь смысл. У меня был еще один вопрос. Могу ли я также создать объект UserManagementDal, который будет схожим с объектами Dal? – zSynopsis

+0

Вы должны явно избегать создания UserManagementDal. Обратите внимание на разницу в именах различных типов услуг: Entity vs. Task vs. Orchestration. Служба сущности отвечает за операции CRUD объекта и, как таковая, отвечает за связь с хранилищем данных. Однако задача, оркестровка, приложение и другие службы НИКОГДА не должны обращаться к базе данных ... только к другим службам. Все это сводится к ответственности и отделяет эти обязанности от разных услуг. Сущности ответственны за взаимодействие хранилища данных низкого уровня. – jrista

+0

UserManagementService - это то, что Томас Эрл вызывает службу задач. Он отвечает за «задачу» более высокого уровня для создания пользователя. Обратите внимание на разницу в виде поведения службы задачи и службы сущности. Служба сущности обеспечивает CRUD низкого уровня, в то время как задача обеспечивает более сложное поведение на уровне более высокого уровня. Сервисы оркестровки (или бизнес-сервисы рабочего процесса) еще более высокого уровня, составляя множество сервисов задач и сущностей даже в более широких областях поведения.Службы приложений будут составлять orch, task и, возможно, службы сущностей для выполнения конкретных приложений. – jrista

0

Я не знаю, что вы имеете в виду Dal, но, как правило, необходимо, чтобы сбалансировать уровень cohesion и coupling в вашем приложении.

В этом случае я задал вопрос, почему экземпляр UserService может проверять другие IUsers. Это похоже на связь, которая может быть проблематичной. Однако я не знаю вашего заявления, поэтому я могу ошибаться.

Проведите диаграмму классов, чтобы было легче увидеть связь между классами.

+1

DAL-Data Access Layer –

0

Вы можете следовать модели, похожей на модель DataContext, за которой следует LINQ2SQL или Entityframework, т.е. иметь контекст, который отслеживает объекты, такие как «Пользователь» и т. Д. В свою очередь, ваш сервисный уровень просто обращается к Контексту с запросом если это необходимо. Таким образом, ваши индивидуальные сущности, такие как объект «Пользователь», не должны иметь прямого знания (связи) с другими «несвязанными» объектами, а Контекст предоставляет сущности клиенту.

Смежные вопросы