2015-01-05 21 views
2

CQRS 원칙의 명령 측면에서 코드 중복에 대한 질문이 있습니다.명령에서 CQRS 코드 중복

https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92

그것의 자신의 클래스에 각 명령을 분리하는이 방법은 데이터에서 개체를 검색 할 때 몇 가지 코드 중복을 줄려고하고 있다는 것을 나에게 보인다

는에서 기사를 따르고 저장.

약간의 문제가있을 수 있지만 예를 들어 전자 메일 주소와 사용자의 마지막 로그인 날짜를 업데이트하려는 명령에 대해 사용자 암호를 재설정하려는 명령이 있다고 가정 해 봅시다.

public class ResetPasswordCommandHandler : CommandHandler<ResetPasswordCommand> 
{ 
    public override void Execute(ResetPasswordCommand command) 
    { 
     **// duplication here** 
     var user = from c in db.Users 
      where c.EmailAddress = command.EmailAddress 
      select c; 

     user.EmailAddress = command.EmailAddress; 
     ... 
     db.Save(); 
    } 
} 

public class UpdateLastLoginCommandHandler : CommandHandler<UpdateLastLoginCommand> 
{ 
    public override void Execute(UpdateLastLoginCommand command) 
    { 
     **// duplication here** 
     var user = from c in db.Users 
      where c.EmailAddress = command.EmailAddress 
      select c; 

     user.LastLogin = DateTime.Now; 
     ... 
     db.Save(); 
    } 
} 

두 명령 모두 사용자의 전자 메일 주소를 기반으로 사용자를 검색합니다. 이제 데이터베이스를 쿼리하기 전에 UI 입력을 트리밍하려면 두 곳에서이를 변경해야합니다.

I 예를 들어 GetUserByEmailAddress 메소드가 있고 해당 IUserRepository를 CommandHandlers의 생성자에 삽입 할 수있는 UserRepository를 작성할 수 있습니다. 그러나 결국 Save, GetById, GetByUsername 등으로 "god 저장소"를 만들지 않을까요?

그리고 리포지토리를 만드는 경우 왜 별도의 쿼리 개체를 만드나요?

이 코드를 어떻게하면 건조하게 만들 수 있습니까?

답변

3

이유는 여러 인터페이스를 구현하는 일에 당신의 명령 처리기를 리팩토링하지 : 당신이 핸들러가 유사한 명령을 처리 할 수있는 명령,

public class UserCommandHandler : CommandHandlerBase, 
            IHandle<ResetPasswordCommand>, 
            IHandle<UpdateLastLoginCommand> 
{ 
    public void Execute(ResetPasswordCommand command) 
    { 
     var user = GetUserByEmail(command.EmailAddress); 

     user.EmailAddress = command.EmailAddress; 
     ... 
     db.Save(); 
    } 

    public void Execute(UpdateLastLoginCommand command) 
    { 
     var user = GetUserByEmail(command.EmailAddress); 

     user.LastLogin = DateTime.Now; 
     ... 
     db.Save(); 
    } 

    private User GetUserByEmail(string email) { 
      return (from c in db.Users 
        where c.EmailAddress = command.EmailAddress 
        select c).FirstOrDefault(); 
    } 
} 

이 방법, 당신은 명령 핸들러 내에서 개인 도우미 메서드를 리팩토링 할 수 있고, 당신은 줄이고 코드 중복. 또한 "god"저장소가 필요하지 않습니다. GetUserByEmail이 나에게 User 얻을 매우 특정 클래스가되도록

개인적으로, 나는 오히려 내가 생성자를 통해에 db 컨텍스트를 주입 별도의 쿼리 클래스와 개인 GetUserByEmail 도우미이있을 것이다.

희망이 도움이됩니다.

+0

여러 인터페이스를 구현하기 위해 명령 처리기를 리팩토링하지 않더라도 비 대한 저장소 대신 "god"명령 처리기 만 만들겠습니까? – user4419743

+0

나는 두 번째 부분을 좋아한다. 확장 메서드를 만들 수 있습니다. 같은 것 : 'public static IQueryable WithEmailAddress (이 IQueryable 소스, 문자열 emailaddress)' – user4419743

1

신 저장소를 생성하지 않습니다. 그것은 명령 유스 케이스 (command use case)에 의해 사용되는 메소드를 갖는 적절한 저장소 일 것이다. 그러나 이는 올바른 리포지토리 패턴을 사용하고 있음을 의미합니다. 즉, IQueryable 또는 EF가 노출되지 않았 음을 의미합니다. 도메인 저장소에는 도메인 유스 케이스에서만 필요한 '쿼리'메소드가 있고 저장소는 도메인 집계 루트 (전체 도메인 객체) 만 처리한다는 점을 기억하십시오.

현재 귀하의 응용 프로그램 서비스에서 EF (결국 DAO)를 사용한다는 것은 응용 프로그램 계층과 아마도 도메인 계층도 EF에 연결됨을 의미합니다. 귀하의 상위 수준 서비스 (명령 처리기는 결국 유스 케이스를 구현하는 서비스 임)의 관점에서 보면 쿼리에 대해 알지 못하는 rdbms가 없기 때문에 쿼리를 실행하지 않아야합니다.

앱이 충분히 간단하거나 미래에 많이 바뀌지 않는다는 것을 안다면 직접 EF를 사용하는 간단한 방법을 사용할 수 있습니다. 그러나이 방법으로 단순화하기로 결정하면 필요하지 않습니다. CQRS 및 메시지 기반 아키텍처.

1

코드 중복이있는 것처럼 보입니다. DRY 보안 주체에 맞지 않는 것처럼 보입니다.하지만 행동에 공유 서비스 저장소 코드를 사용하는 것은 결코 좋은 생각이 아닙니다. 문제 중 하나는 각 동작이 전자 메일 호출로 GetUser의 구현이 약간 다를 수 있다는 것입니다. 하나는 내 이름이 아니고 다른 이름은 필요하지 않을 수 있습니다. 이 코드를 공유함으로써 두 호출을 효과적으로 연결합니다. 이제는 하나의 동작에서 여분의 데이터가 반환 될 수 있습니다. 그러나 이제는 다른 구현체가이 '공유 서비스'를 호출 할 때마다 필요하지 않은 추가 데이터로 인해 오버 헤드가 발생합니다.

저장소 호출을 수행 할 때 코드를 공유하려면 사양 패턴 및/또는 전략 패턴을 사용하십시오. 위의 예제와 같이 까다로운 커플 링 문제가 발생하지 않으며, 보너스로 의도가 구현이 아닌 앞쪽에 위치하므로 코드가 더 잘 읽힐 것입니다.