집계 루트에 다른 AR이 포함되어야하는시기 (그리고 그렇지 않은 경우)


15

먼저 글의 길이에 대해 사과하는 것으로 시작하겠습니다.하지만 실제로 많은 세부 사항을 미리 전달하고 싶기 때문에 의견에 앞뒤로 시간이 걸리지 않습니다.

저는 DDD 접근 방식에 따라 응용 프로그램을 설계하고 있으며, Aggregate Root에 다른 AR이 포함되어야하는지 또는 별도의 "자립형"AR로 남겨 져야하는지 결정하기 위해 어떤 지침을 따를 수 있는지 궁금합니다.

직원들이 하루 종일 시계를 켤 수있는 간단한 시계 응용 프로그램을 예로 들어 보겠습니다. UI를 사용하면 직원 ID 및 PIN을 입력 한 다음 유효성을 검사하고 직원의 현재 상태를 검색 할 수 있습니다. 직원이 현재 시간을 측정 한 경우 UI에 "Clock Out"단추가 표시됩니다. 반대로, 클럭이 맞지 않으면 버튼에 "Clock In"이 표시됩니다. 버튼으로 수행 한 작업은 직원의 상태에도 해당합니다.

애플리케이션은 RESTful 서비스 인터페이스를 통해 노출 된 백엔드 서버를 호출하는 웹 클라이언트입니다. 직관적이고 읽을 수있는 URL을 만드는 첫 단계는 다음과 같은 두 가지 끝점으로 이루어졌습니다.

http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout

참고 : 직원 ID 및 PIN의 유효성이 검사되고 "사용자"를 나타내는 "토큰"이 헤더에 전달 된 후에 사용됩니다. 이는 관리자 또는 감독자가 다른 직원을 시계로 출입 할 수있는 "관리자 모드"가 있기 때문입니다. 그러나이 토론을 위해 간단하게 유지하려고합니다.

서버에는 API를 제공하는 ApplicationService가 있습니다. ClockIn 메서드에 대한 나의 초기 아이디어는 다음과 같습니다.

public void ClockIn(String id)
{
    var employee = EmployeeRepository.FindById(id);

    if (employee == null) throw SomeException();

    employee.ClockIn();

    EmployeeRepository.Save();
}

직원의 시간 카드 정보가 실제로 거래 목록으로 유지된다는 것을 알 때까지는 매우 간단합니다. 즉, ClockIn 또는 ClockOut을 호출 할 때마다 직원의 상태가 직접 변경되지 않고 대신 직원의 TimeSheet에 새 항목이 추가됩니다. 직원의 현재 상태 (시간 기록)는 TimeSheet의 가장 최근 항목에서 파생됩니다.

따라서 위의 코드를 사용하면 내 저장소는 Employee의 지속 가능한 속성이 변경되지 않았지만 새로운 항목이 Employee의 TimeSheet에 추가되어 데이터 저장소에 삽입을 수행한다는 것을 인식해야합니다.

반면 TimeSheet는 집계 루트 인 것처럼 보이고 정체성 (직원 ID 및 기간)을 가지고 있으며 TimeSheet와 동일한 논리를 쉽게 구현할 수 있습니다. (직원 ID).

나는 두 가지 접근법의 장점에 대해 스스로 토론하고 있으며, 첫 문단에서 언급했듯이 어떤 접근법이 문제에 더 적합한지를 결정하기 위해 어떤 기준을 평가해야하는지 궁금합니다.


게시물을 편집 / 업데이트 했으므로 질문이 명확하고 더 나은 시나리오를 사용하는 것이 좋습니다.
SonOfPirate

답변:


4

시간 추적 서비스를 구현하려는 경향이 있습니다.

public interface ITimeSheetTrackingService
{
   void TrackClockIn(Employee employee, Timesheet timesheet);

   void TrackClockOut(Employee employee, Timesheet timesheet);

}

직원의 책임도 아니고 시간 기록도 시간표의 책임이라고 생각하지 않습니다.


3

집계 루트는 서로를 포함하지 않아야합니다 (다른 ID를 포함 할 수 있음).

첫째, TimeSheet는 실제로 집계 루트입니까? 그것이 정체성을 가지고 있다는 사실은 반드시 AR이 아니라 실체가됩니다. 규칙 중 하나를 확인하십시오.

Root Entities have global identity.  Entities inside the boundary have local 
identity, unique only within the Aggregate.

TimeSheet의 ID를 직원 ID 및 기간으로 정의합니다. 이는 TimeSheet가 기간이 로컬 ID 인 직원의 일부임을 나타냅니다. 이것은 Time Clock 응용 프로그램이므로 Employee의 주요 목적은 TimeSheets의 컨테이너입니까?

AR이 필요하다고 가정하면 ClockIn 및 ClockOut은 직원보다 TimeSheet 작업처럼 보입니다. 서비스 계층 방법은 다음과 같습니다.

public void ClockIn(String employeeId)
{
    var timeSheet = TimeSheetRepository.FindByEmployeeAndTime(employeeId, DateTime.Now);    
    timeSheet.ClockIn();    
    TimeSheetRepository.Save();
}

Employee와 TimeSheet 모두에서 시계 상태를 추적 해야하는 경우 도메인 이벤트를 살펴보십시오 (Evans의 책에는 없다고 생각하지만 온라인에는 많은 기사가 있습니다). Employee.ClockIn ()은 EmployeeClockedIn 이벤트를 발생시켜 이벤트 처리기가 선택하고 TimeSheet.LogEmployeeClockIn ()을 호출합니다.


나는 당신의 요점을 참조하십시오. 그러나 직원이 언제 그리고 어느 때에 시계를 켤 수 있는지를 제한하는 특정 규칙이 있습니다. 이러한 규칙은 대부분 직원의 현재 상태, 예를 들어 종료, 이미 시계로 작동하는지, 해당 날짜에 작업하도록 예정되어 있는지 또는 현재 변화 등 TimeSheet에이 지식이 있어야합니까?
SonOfPirate 16:10에

3

저는 DDD 접근 방식에 따라 응용 프로그램을 설계하고 있으며, Aggregate Root에 다른 AR이 포함되어야하는지 또는 별도의 "자립형"AR로 남겨 져야하는지 결정하기 위해 어떤 지침을 따를 수 있는지 궁금합니다.

집계 루트는 다른 집계 루트를 포함해서는 안됩니다.

설명에 직원 엔터티 및 작업 표 엔터티가 있습니다. 이 두 개체는 서로 다르지만 서로에 대한 참조를 포함 할 수 있습니다 (예 : Bob의 작업 표).

기본 모델링이 그 정도입니다.

전체 근본 질문은 약간 다릅니다. 이 두 엔터티가 서로 트랜잭션 적으로 구별되는 경우 두 개의 별개의 집계로 올바르게 모델링 될 수 있습니다. 트랜잭션 별개로, 직원의 현재 상태와 TimeSheet의 현재 상태를 동시에 알아야하는 비즈니스 불변이 없음을 의미합니다.

설명하는 내용에 따라 Timesheet.clockIn 및 Timesheet.clockOut은 직원의 데이터를 확인하여 명령이 허용되는지 여부를 확인할 필요가 없습니다. 따라서이 많은 문제에 대해 두 가지 다른 AR이 합리적으로 보입니다.

AR 경계를 고려하는 또 다른 방법은 어떤 종류의 편집이 동시에 수행 될 수 있는지 묻는 것입니다. HR이 직원의 프로필을 처리하는 동시에 관리자가 직원을 감시 할 수 있습니까?

반면에 (그리고 게시물의 궁극적 인 질문입니다) TimeSheet는 그것이 루트 인 것처럼 보이고 정체성 (직원 ID 및 기간)이있는 것처럼 보입니다.

아이덴티티는 단지 엔티티라는 것을 의미합니다. 비즈니스는 불변 적이므로 별도의 집계 루트로 간주되어야하는지 여부를 결정합니다.

그러나 직원이 언제 그리고 어느 때에 시계를 켤 수 있는지를 제한하는 특정 규칙이 있습니다. 이러한 규칙은 대부분 직원의 현재 상태, 예를 들어 종료, 이미 시계로 작동하는지, 해당 날짜에 작업하도록 예정되어 있는지 또는 현재 변화 등 TimeSheet에이 지식이 있어야합니까?

아마도-골재가되어야합니다. 반드시 작업 표가되어야한다는 의미는 아닙니다.

즉, 작업 표 수정 규칙이 직원 엔터티의 현재 상태에 의존하는 경우 직원과 작업 표는 반드시 동일한 집계에 속해야하며 전체 집계는 규칙이 따라 갔다.

집합체에는 하나의 루트 엔터티가 있습니다. 그것을 식별하는 것은 퍼즐의 일부입니다. 직원이 둘 이상의 작업 표를 가지고 있고 둘 다 동일한 집계의 일부인 경우 작업 표는 루트가 아닙니다. 즉, 응용 프로그램이 명령을 작업 표에 직접 수정하거나 디스패치 할 수 없음을 의미합니다. 명령을 루트 오브젝트 (아마도 Employee)로 디스패치해야합니다.

또 다른 점검은 작업 표 작성 방법을 고려하는 것입니다. 직원이 시계를 볼 때 암시 적으로 생성되면 집계의 하위 엔터티라는 또 다른 힌트입니다.

제쳐두고, 당신의 집계가 그들 만의 시간 감각을 가져야 할 것 같지 않습니다. 대신, 시간은 그들에게 전달되어야합니다

employee.clockIn(when);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.