提问人:Amirhosein Al 提问时间:8/7/2021 最后编辑:John KugelmanAmirhosein Al 更新时间:8/10/2021 访问量:50
在保持数据完整性的同时对循环依赖关系进行建模
Modeling circular dependencies while maintaining data integrity
问:
我正在设计一个音乐信息系统。我有几个相互连接的实体。
以下是域代码的一部分。
class Album {
private Set<Track> tracks;
private boolean published;
public Set<Track> getTracks() {
return this.tracks;
}
public boolean isPublished() {
return this.published;
}
public void publish() {
System.out.println("Album.publish() called");
this.published = true;
this.tracks.forEach(track -> track.publish());
}
}
class Track {
private boolean published;
private Album album;
public boolean isPublished() {
return this.published;
}
public Album getAlbum() {
return this.album;
}
public void publish() {
// if track is single (this.album == null), set published to true
// if track is part of an album and the album is NOT published, return;
// if track is part of an album and the album is published, set published to true
if(this.album != null && !this.album.isPublished())
return;
this.published = true;
}
}
Track 是一个独立的实体。它可以是单个曲目(即没有专辑)。所以这个属性实际上是需要的。
一个域规则是,当专辑被存档(即未发布)时,它的曲目也不能发布,如果专辑被发布,它的任何曲目都可以被发布或存档。
问题在于,当一张专辑被发布时(例如),它的曲目方法也会被调用。但是,根据专辑已有的副本(未发布)检查专辑是否已发布。
我该如何解决问题?album
album1.publish()
publish()
track1.publish()
答:
0赞
rocket-3
8/10/2021
#1
如果按行为拆分域模型实体,则可以摆脱所描述的限制
让我们为这些实体提供一些接口:
interface AlbumId{
String asString();
AlbumId Absent = () -> "NO ALBUM AT ALL";
}
interface Publication{
void publish() throws Exception;
void archive() throws Exception;
boolean published();
}
interface Track{
TrackId id();
AlbumId albumId(); //smart type (as DDD suggest), therefore, no more nulls
}
现在,您可以通过创建类来强制执行规则,这些类将为您提供可以发布的曲目列表:
public class TracksReadyToPublishOf implements Supplier<Map<TrackId, TrackPublication>>{
//this class may access to cache and have dosens of other optimizations
public TracksReadyToPublishOf(AlbumId id){...}
@Override public get(){...}
}
然后,您可以在任何地方重复使用您的代码来检查您的规则:
public class TrackPublication implements Publication {
private final Track track;
private final Supplier<Map<TrackId, TrackPublication>> allowedTracks;
//easy for unit testing
public SmartTrackPublication(Track track, Supplier<Map<TrackId, TrackPublication>> allowedTracks){
this.track = track;
this.allowedTracks = allowedTracks;
}
public SmartTrackPublication(Track track){
this(track, new TracksReadyToPublishOf(track.albumId());
}
@Override
public publish() throws AlbumArchivedException{
if(this.albumId != AlbumId.Absent){
if(!this.allowedTracks.get().containsKey(this.track.id())){
throw new AlbumArchivedException();
}
}
this.allowedTracks.get().get(this.id()).publish();
}
}
对于专辑发行:
public class AlbumPublication implements Publication{
private final AlbumId id;
private final Producer<Map<TrackId, TrackPublication>> tracks
private AlbumWithTracks(AlbumId id, Producer<Map<TrackId, TrackPublication>> tracks){
this.id = id;
this.tracks = tracks;
}
public AlbumWithTracks(AlbumId id){
this(id, new TracksReadyToPublishOf(id))
}
...
@Override publish() throws Exception{
//code for publishing album
for(TrackPublication t : Arrays.asList(
this.tracks.get()
)){
t.publish(); //track can publish anyway if it presents in list above
}
}
}
评论
Album
Album
Track
Album
Track
addTrack(Track)
Album
Album