应该如何汇总来自子实体的公开信息? [英] How should aggregate expose info from child entity?

查看:68
本文介绍了应该如何汇总来自子实体的公开信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[从此问题开始跟进实体应该具有方法,如果可以的话,如何防止将其称为外部集合]



详细了解聚合根如何将其子实体的数据暴露给外部世界;



因此,为了便于讨论,我们考虑以下规则:



p>


  1. 人有几种沟通渠道:电话,电子邮件,facebook帐户(也可以是其中每种的集合,但为简单起见,可以说

  2. 人员可以选择将这些频道中的任何一个设为公开,以便其他人可以与他联系或将其中的任何一个设为私有,以便无法对其进行ping通

  3. 人员可以选择根本不联系的全局设置。在那种情况下,它禁止将任何电话,电子邮件,facebook切换到公开状态。

[注意:现在显示可能不是最好的显示,但是暂时不要进行重塑,而是着重于公开子实体信息]



让我们说我们已经合计了根人员(c#代码),仅以 Phone 实体为例,其他逻辑相同:

 班级人员{
...
私人电话{ set;}

public bool WantsToBeContactedAtAll {get; }

public void ExposePhoneNumberPublic(){
if(!this.WantsToBeContactedAtAll)
抛出新的SomeError(不允许。);

this.Phone.PublishPhoneNumber(true);
}

public void HidePhoneNumber(){
this.Phone.PublishPhoneNumber(false)
}

}

class Phone {
//这是标识符
public readonly string PhoneNumber {get;私人套装; }
公共字符串说明{get;私人套装; }
public boolean ShouldBePublished {get;私人套装; }

公共电话(字符串phoneNumber,字符串描述,bool shouldBePublished){
//设置值
}

公共无效PublishPhoneNumber(布尔优先级) {
this.ShouldBePublished =偏好;
}

因此,我们要防止的是某人在做:

  Person Adam = new Person(...); 
Adam.Phone.PublishPhoneNumber(true);

但是现在,我们仍然需要来自 Adam.Phone 如果没有其他要求,则供存储库在保存总计时访问它:

  _personRepository.Add(Adam); 

问题:


  1. 如何公开 Person.Phone 信息?


  2. 我们应该将Phone属性的某些副本作为结构(值对象)公开吗?


  3. Phone 作为个人类型包含在 Person 内,并公开另一个 PhoneReadOnly 键入只不过是带有属性和吸气剂的类。


另一种询问所有问题的方式是:至少存储库如何读取才能保存 Person Person.Phone 信息?



请把我当成一个白痴,并详细说明。



谢谢

解决方案


应如何汇总子实体的信息?


以不允许调用者更改聚合状态的方式。




  • 传递原语值

  • 传递对不可变对象的引用

  • 传递对象的副本



信息副本很好,因为您无法通过更改数据副本来更改我的状态。引用不可变的对象很好,因为您根本无法更改它们,因此也无法更改我的状态。但是,给我一个可变状态的引用会增加程序员出错的可能性。



让我们暂时考虑一下存储库示例-记住,存储库是用来给在应用程序中,所有的聚合只是内存集合中某些庞大成员的错觉。为了支持这种错觉,存储库需要两个功能:一个从稳定的数据存储中获取表示,并从中创建构成集合的域模型实体,另一个从集合中构建并从中构建表示的表示,



让我们假装我们有一些非常幼稚的聚合,它只是整数数组

  class集合{
int []省;
}

然后我们想象存储库可能需要加载和存储此函数合计

 合计a = Aggregate.from(state)
int [] state = a.state

现在,如果我们尝试作弊会怎样?

  int [] state = a.state; 
state [0] = 12345;

a 是否更改?因为我们希望域模型成为世界状况的权威,所以答案最好是否。反过来,这意味着聚合不会引用其自身的数组,而是引用该数组的副本。



如果我们考虑一个

  class聚合{
Child [] children;
}

那么总收益是多少?它不是自己的数组,因为这将允许客户端通过替换Child来更改聚合。但是它也不能只复制数组,因为我们可以调用子数组元素之一上的方法来更改自身,这将间接更改聚合的状态。



因此,我们不返回孩子的数组,而是返回孩子的描述的数组。这是一种深复制。描述包含数据副本,但没有 references -没有链接到实体本身内部的东西-因此可以安全地将描述提供给调用方,调用方可以执行其操作与此类似(包括将说明粘贴到文档存储中以便以后恢复)。


[ Follow up from this question Should entity have methods and if so how to prevent them from being called outside aggregate ]

I am trying to understand in full details how aggregate root exposes data from its child entities to outer world; in particular, at least repository will need that info in order to be able to save it.

So, for the sake of the argument, lets consider these rules:

  1. Person have a few channels of communication: phone, email, facebook account (might as well be a collection of each of those but for simplicity lets say its just one of each).
  2. Person can choose to make any of those channels public so that other Persons can contact him or make any of those private so that he can't be pinged through that particular channel.
  3. Person can choose global setting of not being contacted at all. In that case, its forbidden to switch any of the phone, email, facebook to public.

[ Note: the model i'll show now might not be the best one but lets leave remodeling aside for now and focus on exposing child entity infos ]

lets say we have aggregate root Person (c# code), only with example for Phone entity as others are the same logic:

class Person {
  ...
  private Phone Phone  { get; set;}

  public bool WantsToBeContactedAtAll { get; }

  public void ExposePhoneNumberPublic() {
    if(!this.WantsToBeContactedAtAll)
      throw new SomeError("Not allowed.");

    this.Phone.PublishPhoneNumber(true);
  }

  public void HidePhoneNumber() {
    this.Phone.PublishPhoneNumber(false)
  }

}

class Phone {
  //this is identifier
  public readonly string PhoneNumber { get; private set; }
  public string Description { get; private set; }
  public boolean ShouldBePublished { get; private set; }

  public Phone(string phoneNumber, string description, bool shouldBePublished) {
    //set values
  }

  public void PublishPhoneNumber(bool preference){
    this.ShouldBePublished = preference;
  }

So, what we want to prevent is someone doing:

Person Adam = new Person(...);
Adam.Phone.PublishPhoneNumber(true);

But now, we still need info from Adam.Phone if for nothing else, then for the repository to access it when saving aggregate:

_personRepository.Add(Adam);

Questions:

  1. How to expose Person.Phone info?

  2. Should we expose some copy of the Phone property as a struct (value object)?

  3. Have Phone as private type within Person aggregate and expose another PhoneReadOnly type what would be just a class with properties and getters.

Another way of asking those all question is: how can at least repository read Person.Phone information that it needs in order to be able to save Person?

Please treat me as a complete idiot and explain in details.

Thanks

解决方案

How shoud aggregate expose info from child entity?

In a way that doesn't allow the caller to change the state of the aggregate.

  • Pass a primitive value
  • Pass a reference to an immutable object
  • Pass a copy of an object

Copies of information are fine, because you can't change my state by changing your copy of my data. References to immutable objects are fine, because you can't change them at all, therefore you can't change my state. But giving you a reference to my mutable state increases the odds of a programmer error.

Let's consider the repository example for a moment -- repositories, remember, are used to give the application the illusion that all of the aggregates are just members of some vast, in memory collection. To support this illusion, the repository needs two functions -- one that takes a representation from our stable data store and creates from it the domain model entities that make up the aggregate, and another that takes the aggregate and constructs from it the representation to put in the data store.

Let's pretend that we had some really naive aggregate that was just an array of integers

class Aggregate {
    int [] State;
}

And then we imagine the functions that a repository might need to load and store this aggregate

Aggregate a = Aggregate.from(state)
int [] state = a.state

Now, what happens if we try to cheat?

int [] state = a.state;
state[0] = 12345;

Did a change? Since we want the domain model to be the authority for the state of the world, the answer had better be "no". Which in turn means that the aggregate doesn't yield a reference to its own array, but instead a copy of that array.

The same principle applies if we think about an aggregate with an array of child entities.

class Aggregate {
    Child [] children;
}

So what does this aggregate yield? Not it's own array, because that would allow the client to change the aggregate by replacing a Child. But it can't just copy the array either, because we could call methods on one of the child array elements to change itself, which would indirectly change the state of the aggregate.

So we don't return an array of children, we return an array of descriptions of children. It's a sort of "deep copy". The descriptions contain copies of data, but no references -- nothing that links back to the internals of the entity itself -- and so it is safe to yield the description to a caller, who can do what they like with it (including sticking the description into a document store for later recovery).

这篇关于应该如何汇总来自子实体的公开信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆