2011-10-31 2 views
3

자바에서는 immutable POJO의 계층 구조를 사용하여 도메인 모델을 표현하고자합니다.불변의 비즈니스 객체와 MessagePack 메시지 사이의 자동 변환

final ServiceId id = new ServiceId(ServiceType.Foo, "my-foo-service") 
final ServiceConfig cfg = new ServiceConfig("localhost", 8080, "abc", JvmConfig.DEFAULT) 
final ServiceInfo info = new ServiceInfo(id, cfg) 

이러한 POJO에는 모두 getter 또는 setter가없는 공개 최종 입력란이 있습니다. (getter의 팬이라면, 게터로 필드가 비공식적 인 것으로 가정하십시오)

MessagePack 라이브러리를 사용하여 이러한 객체를 직렬화하여 네트워크를 통해 전달하고 ZooKeeper에 저장하고 싶습니다. 노드 등.

문제는 MessagePack이 공개 필드, 비 최종 필드의 직렬화 만 지원하므로 비즈니스 객체를있는 그대로 직렬화 할 수 없다는 점입니다. 또한 MessagePack은 enum을 지원하지 않으므로 직렬화를 위해 enum 값을 int 또는 String으로 변환해야합니다. (당신은. 당신의 enum들에 주석을 추가하려면 아래 내 코멘트를 참조하면 네, 그것은, 않습니다.)

이 처리하기 위해 내가 각 Business Object 사이의 변환에, "메시지"개체의 손으로 쓴 해당 계층 구조를 가지고 및 해당 메시지 객체. 분명히 이것은 많은 양의 중복 된 코드를 유발하고 사람의 실수로 필드가 누락 될 수 있기 때문에 이상적이지 않습니다.

이 문제에 대한 더 좋은 해결책이 있습니까?

  • 컴파일 타임에 코드가 생성됩니까?
  • 런타임시 적절한 직렬화 가능 클래스를 생성하는 몇 가지 방법은 무엇입니까?
  • MessagePack에서 포기 하시겠습니까?
  • 불변의 것을 포기하고 내 비즈니스 개체에 enum을 지정 하시겠습니까?
  • 변경할 수있는 객체 (메시지 객체)를 변경할 수없는 객체 (비즈니스 객체)로 래핑 할 수있는 일반 래퍼 라이브러리가 있습니까? 나는 자동으로 가까이 솔루션에 저를 얻을 수있는 자바 빈,로/불변의 객체를 변환 할 수 있습니다, 그래서 만약

는 MessagePack 또한, 자바 콩의 직렬화합니다 (@MessagePackBeans 주석을 사용하여)를 지원합니다.

+0

[@OrdinalEnum] (https://github.com/msgpack/msgpack-java/blob/master/src/main)을 추가하면 MessagePack이 'enum'유형을 지원할 수 있다는 것을 시행 착오를 통해 발견했습니다. /java/org/msgpack/annotation/OrdinalEnum.java) 주석을 모든 열거 형에 적용합니다. @Delegate 주석이하는 일을 해결하기 위해 이제 ... –

답변

0

응용 프로그램의 읽기 및 쓰기 문제를 구분하지 않고 병합 한 것처럼 들립니다. 이 시점에서 CQRS를 고려해야합니다.

내 경험에 비추천 도메인 개체는 거의 항상 감사 이야기 (요구 사항)에 첨부되거나 조회 데이터 (열거 형)입니다.

도메인은 대부분 변경 가능해야하지만 getter 및 setter가 필요하지는 않습니다. 대신 도메인에 수정 된 도메인을 생성하는 객체에 동사가 있어야하며 도메인에서 흥미로운 것이 발생하면 이벤트를 발생시킬 수 있습니다 (비즈니스 - 비즈니스 == 귀하의 시간 동안 돈을 지불하는 사람에게 흥미 롭습니다). 아마도 도메인 객체가 아닌 와이어를 통해 전달하려는 이벤트 일 것입니다. 어쩌면 명령 일 수도 있습니다 (이벤트와 비슷하지만 소스는 도메인이있는 바운드 컨텍스트의 외부 에이전트이며 이벤트는 모델의 바운드 컨텍스트 내부에 있습니다).

감사 기록 ​​(감사 내역 완수)이기도 한 이벤트를 유지하는 서비스 (다른 명령은 명령 유지) 서비스를 가질 수 있습니다.

이벤트를 버스로 푸시하는 이벤트 처리기를 사용할 수 있습니다. 이러한 이벤트에는 단순 정보 또는 엔티티 ID가 포함되어야합니다. 이러한 이벤트에 응답하는 서비스는 제공된 정보를 사용하여 직무를 수행하거나 주어진 ID를 사용하여 필요한 정보를 쿼리해야합니다.

도메인 모델의 내부 상태가 노출되어서는 안됩니다. 그렇게함으로써 캡슐화를 깨뜨리는 것입니다. 실제로 그렇게하는 것이 바람직하지 않습니다. 내가 너라면 Axon Framework을 살펴볼 것이다. MessagePack 혼자만의 것을 얻을 수 있습니다.

+0

CQRS는 여기 (서버가 불변의 구성 정보를 서로에게 전달하고 실제로 어떤 도메인 수준의 이벤트도 일어나지 않는) 나의 사용 사례에 실제로 적합하지 않습니다. 하지만 CQRS에 대한 훌륭한 설명과 Axon Framework에 대한 링크를 알려 드리겠습니다. 이전에는 그 프레임 워크를 본 적이 없었습니다. 그것은 다른 프로젝트에 매우 유용 할 것 같습니다. –

1

필자는 최근에 당신이 설명하는 것을 거의 정확하게 수행하는 프로젝트를 만들었습니다. 불변의 데이터 모델을 사용하면 큰 이점을 얻을 수 있지만, 많은 serialization 기술은 이후 변경 사항으로 불변으로 접근하는 것으로 보입니다. 나는 이것을 고칠 수있는 것을 원했다.

내 프로젝트 Grains은 코드 생성을 사용하여 도메인 모델의 불변 구현 을 만듭니다. 이 구현은 다른 직렬화 프레임 워크에 적용 할 수있을 정도로 충분히 포괄적입니다. MessagePack, Jackson, Kryo 및 표준 Java 직렬화가 지금까지 지원됩니다.

도메인 모델을 설명하는 일련의 인터페이스를 작성하기 만하면됩니다. 예를 들어

public interface ServiceId { 
    enum ServiceType {Foo, Bar} 

    String getName(); 
    ServiceType getType(); 
} 

public interface ServiceConfig { 
    enum JvmConfig {DEFAULT, SPECIAL} 

    String getHost(); 
    int getPort(); 
    String getUser(); 
    JvmConfig getType(); 
} 

public interface ServiceInfo { 
    ServiceId getId(); 
    ServiceConfig getConfig(); 
} 

곡물 Maven 플러그인은 컴파일시 불변 이러한 인터페이스의 구현을 생성한다. (생성하는 소스는 사람이 읽을 수 있도록 설계되었습니다.) 그런 다음 개체의 인스턴스를 만듭니다. 내가 아는 당신의 public final 필드와

ServiceIdGrain id = ServiceIdFactory.defaultValue() 
    .withType(ServiceType.Foo) 
    .withName("my-foo-service"); 

ServiceConfigBuilder cfg = ServiceConfigFactory.newBuilder() 
    .setHost("localhost") 
    .setPort(8080) 
    .setUser("abc") 
    .setType(JvmConfig.DEFAULT); 

ServiceInfoGrain info = ServiceInfoFactory.defaultValue() 
    .withId(id) 
    .withConfig(cfg.build()); 

간단하지,하지만, 상속 및 구성은 게터 및 setter없이 가능하지 않습니다 :이 예 은 두 건설 패턴을 보여줍니다. 그리고, 이러한 개체를 쉽게 읽고 MessagePack에 기록 된 다음 곡물 프레임 워크는 당신을 위해 작동하지 않습니다

MessagePack msgpack = MessagePackTools.newGrainsMessagePack(); 

byte[] data = msgpack.write(info); 
ServiceInfoGrain unpacked = msgpack.read(data, ServiceInfoGrain.class); 

경우, 그 MessagePack templates을 검사 주시기 바랍니다. 리플렉션을 사용하여 손으로 작성한 도메인 모델의 최종 필드를 설정하는 일반 TemplateBuilder을 작성할 수 있습니다. 트릭 은 사용자 정의 빌더를 등록 할 수있는 사용자 정의 TemplateRegistry을 작성하는 것입니다.

+0

굉장! 정말 유용하게 보입니다. –