bestsource

봄과 빈혈 영역 모델

bestsource 2023. 8. 17. 21:34
반응형

봄과 빈혈 영역 모델

Spring/Hibernate 스택 개체를 다음과 같이 패턴화하는 경향이 있습니다.

  • Foo 컨트롤러가 "FooService"에 호출합니다.
  • FooService는 FooRepository.getById() 메서드를 호출하여 일부 Foo를 가져옵니다.
  • FooRepository는 Foo 개체를 로드하기 위해 일부 Hibernate 호출을 수행합니다.
  • FooService는 Foo와 몇 가지 상호 작용을 수행합니다.트랜잭션에서 함께 수행해야 하는 작업을 처리하기 위해 관련된 Transactional Foo Service를 사용할 수 있습니다.
  • FooService는 FooRepository에 Foo를 저장할 것을 요청합니다.

여기서 문제는 푸들이 진짜 논리를 가지고 있지 않다는 것입니다.예를 들어 Foo가 만료될 때마다 전자 메일을 보내야 하는 경우 Foo.expire()에 대한 호출이 없습니다.FooService.expireFoo(fooId)로 전화가 왔습니다.이는 다음과 같은 다양한 이유 때문입니다.

  • Foo에서 다른 서비스와 물건을 얻는 것은 귀찮습니다.이것은 Spring bean이 아니고 Hibernate에 의해 로딩되었습니다.
  • Foo가 거래적으로 몇 가지 일을 하도록 하는 것은 짜증나는 일입니다.
  • 푸가 자신을 구할 때를 선택하는 것에 책임을 져야 하는지를 결정하는 것은 어렵습니다.foo.setName()을 호출하는 경우, foo가 변경 사항을 유지해야 합니까?foo.save()를 부를 때까지 기다려야 합니까?foo.save()는 FooRepository.save(이)를 호출해야 합니까?

그래서 이런 이유로, 제 봄 도메인 객체는 기본적으로 검증 논리를 가진 미화된 구조인 경향이 있습니다.아마도 이것은 괜찮을 것입니다.아마 절차 코드로 웹 서비스도 괜찮을 것입니다.새로운 기능이 작성되면 동일한 오래된 개체를 새로운 방식으로 처리하는 새로운 서비스를 만들 수 있습니다.

하지만 저는 이런 종류의 디자인에서 벗어나고 싶습니다. 그리고 저는 다른 스프링 사용자들이 그것에 대해 무엇을 하는지 궁금합니다.당신은 로드 타임 직조와 같은 화려한 속임수로 그것과 싸우나요?또 다른 묘기가 있습니까?절차상 문제가 없다고 생각하십니까?

Spring은 AOP를 사용하여 최대 절전 모드 인스턴스에 서비스를 주입할 수 있습니다.인터셉트를 사용하여 최대 절전 모드에서도 동일한 작업을 수행할 수 있습니다.

https://web.archive.org/web/20131209103730/http ://www.jblewitt.com/blog/ ?p=http를 참조하십시오.

"Foo가 트랜잭션으로 몇 가지 작업을 수행하도록 하는 것은 성가신 일입니다."와 관련하여, 서비스 구현이 트랜잭션에 대해 알고/관심을 가질 것으로 예상합니다. 현재 도메인 모델 내에서 서비스 인터페이스를 사용하고 있다면 이는 그다지 성가신 일이 아닐 것입니다.

도메인 모델을 저장해야 하는 시기를 결정하는 것은 모델이 무엇인지, 사용 중인 작업에 따라 달라질 수 있습니다.

FWIW 저는 똑같은 종류의 빈혈 구조물을 생산하는 경향이 있습니다. 하지만 저는 거기에 도달하고 있습니다. 이제 더 합리적인 방법으로 그것을 하는 것이 가능하다는 것을 압니다.

당신의 애플리케이션은 절차적 코딩 원칙을 중심으로 설계된 것처럼 들립니다.이것만으로도 당신이 하려는 모든 객체 지향 프로그래밍을 방해할 수 있습니다.

Foo가 통제하는 행동이 없을 수도 있습니다.비즈니스 로직이 최소인 경우 도메인 모델 패턴을 사용하지 않는 도 허용됩니다.트랜잭션 스크립트 패턴은 때때로 의미가 있습니다.

문제는 그 논리가 커지기 시작할 때 발생합니다.트랜잭션 스크립트를 도메인 모델로 리팩터링하는 것은 가장 쉬운 일은 아니지만, 가장 어려운 일은 아닙니다.Foo를 둘러싼 수많은 논리가 있다면 도메인 모델 패턴으로 이동하는 것을 추천합니다.캡슐화의 이점을 통해 현재 진행 중인 작업과 관련된 작업을 쉽게 이해할 수 있습니다.

갖고 싶다면,Foo.Expire()Foo와 .OnExpiration연결합니다.foo.OnExpiration += FooService.ExpireFoo(foo.Id)객체 생성에 대해, 아마도 사용된 공장을 통해.FooRepository.

정말로 먼저 생각해 보세요.모든 것이 이미 제자리에 있을 가능성이 높습니다현재로는.

행운을 빕니다.

저는 당신의 문제를 해결할 간단한 리팩터링 패턴이 있다고 생각합니다.

  1. 리포지토리에 서비스를 주입합니다.
  2. Foo를 반환하기 전에 Foo 서비스를 사용합니다.
  3. 이제 Foo컨트롤러가 FooRepository에서 적절한 Foo를 요청하도록 하십시오.
  4. 이제 당신이 원하는 방법을 푸라고 부르세요.자체적으로 구현할 수 없는 경우 FooService에서 적절한 방법을 호출하도록 합니다.
  5. 이제 Foo의 "버킷 브리지" 메서드를 통해 FooService에 대한 모든 호출을 제거합니다(이 메서드는 서비스에 매개 변수를 전달합니다).
  6. 이제부터는 메소드를 추가하고 싶을 때마다 Foo에 추가합니다.
  7. 성능상의 이유로 꼭 필요한 경우에만 서비스에 추가하십시오.항상 그렇듯이 모델 개체를 통해 이러한 메서드를 호출해야 합니다.

이를 통해 보다 풍부한 도메인 모델로 발전할 수 있습니다.또한 모든 DB 종속 코드가 FooService 구현에 남아 있으므로 단일 책임 원칙을 유지하고 비즈니스 로직을 FooService에서 Foo로 마이그레이션할 수 있습니다.백엔드를 다른 DB나 메모리 내 또는 모의 테스트(테스트용)로 전환하려는 경우 FooService 계층 외에는 변경할 필요가 없습니다.

저는 FooService가 ORM에서 수행하기에는 너무 느린 DB 호출을 수행한다고 가정합니다. 예를 들어 지정된 Foo와 속성 X를 공유하는 가장 최근의 Foo를 선택하는 것입니다.그것이 제가 대부분의 작품을 본 방식입니다.


다음 대신:

class Controller{
    public Response getBestStudentForSchool( Request req ){
        Student bestStudent = StudentService.findBestPupilForSchool( req.getParam( "schlId" ).asInt() );
        ...
    }
}

다음과 같은 방향으로 이동합니다.

class Controller{
    public Response getBestStudentForSchool( Request req ){
        School school = repo.get( School.class, req.getParam( "schlId" ).asInt() ); 
        Student bestStudent = school.getBestStudent();
        ...
    }
}

당신이 동의하기를 바라는 것은 이미 더 풍부해 보입니다.지금 다른 데이터베이스 호출을 하고 있지만 세션에서 학교를 캐시한 상태로 유지하면 패널티가 무시됩니다.어떤 진정한 OOP 모델도 당신이 사용하고 있는 빈혈 모델보다 효율성이 떨어질까 두렵지만, 코드 명확성을 통한 버그 감소는 그만한 가치가 있어야 합니다.언제나처럼 YMMV.

Doug Rosenberg와 Matt Stephens의 UML 활용 사례 기반 객체 모델링 책을 추천합니다.그것은 빈혈 도메인 모델에 대해서도 이야기하는 소프트웨어 개발 방법론인 ICONIX 프로세스에 대해서도 이야기합니다.또한 Martin Fowler가 웹 사이트 https://www.martinfowler.com/bliki/AnemicDomainModel.html 에서 개발한 주제이기도 합니다. 하지만 Spring Framework 및/또는 Spring Boot를 사용할 때 어떻게 달성할 수 있는지도 알아보려고 합니다.

언급URL : https://stackoverflow.com/questions/1304245/spring-and-the-anemic-domain-model

반응형