将域实体的可变属性存储为值对象是否可以?

时间:2012-03-17 06:32:01

标签: php architecture separation-of-concerns

我希望UserEntity的某些部分能够更改和传递,并且某些部分应该保持不变。

例如,我从不想改变我的UserEntity的id,但是电子邮件或密码之类的东西可能会经常更改,并且也可以被UserEntity之外的其他对象使用。

这样做的一个例子是创建UserEntity时。由于UserEntity在没有id的情况下不能存在,我的控制器可以创建一个UserData对象来标准化UserEntity属性。在映射器在db中创建实体之后,它将创建一个新的UserEntity并传​​入构造函数中的id和UserData对象。

当UserEntity需要电子邮件或密码等信息时,它只能查看其UserData。

似乎更便携,但这有点过分吗?有更好的解决方案吗?

注意

  • 我认为这可能是好的原因:可变字段的值需要标准化......有时这些字段需要在实体本身之外传递。例如,在创建实体之前。通过创建一个可以传递的值对象,我们提供了一个标准化的点,可以从任何地方分配这些值,以及可以在实体外部传递的东西。

  • 通过“标准化”,我的意思是我的信息需要统一,无论它存在于何处。例如,email需要始终为n长度,并且在有效格式中,name始终需要n长度等。我的目标是我喜欢能够在一个地方设置这些“规则”......并且由于UserEntity的这些属性(可变的)存在于实体本身之外,有时候,它们可能会以自己的价值独立存在宾语。

2 个答案:

答案 0 :(得分:1)

我认为没有"一种真正的方式"这样做(无论你读到什么)......如果它在你的模型中有意义,那对我来说听起来不错。当你说"很多这些领域需要标准化时,我不确定你的意思。以及为什么不能作为UserEntity的一部分来完成,但无论如何。也就是说,如果没有一个完全独立的对象类,你很可能完成你正在尝试做的事情。

评论/批评:

你所暗示的并不是真正符合严格的对象"模型,即UserData只是由UserEntity的真正属性组成,并且与这些属性没有其他潜在的关系。

我不确定为什么你需要一个单独的对象在实体外传递...如果你需要数据,为什么你不能绕过UserEntity并从那里访问它?在将数据传递给UserEntity构造函数之前,您需要做什么才能通过在stdClass实例中收集数据然后在UserEntity中处理它来轻松完成?


如果是我,我会做更多类似的事情(例如,创建一个新用户):

<?
// assume an appropriately defined UserEntity class...

// I'm using stdClass just to keep the parameters together to pass all at once
// I'm assuming some basic user data passed from the browser
$user_data = (object) array(
    'email' => $_REQUEST['email'],
    'name' => $_REQUEST['name'],
    'password' => $_REQUEST['password'],
    'confirm_password' => $_REQUEST['confirm_password']
);

/*
validateData is static so it can be called before you create the new user
It takes the $user_data object to validate and, if necessary, modify fields.
It also takes a $create flag which indicates whether the data should be
checked to make sure all of the necessary fields are there to create the user
with.  This allows you to call it on update with the $create flag unset and it
will pass validation even if it's missing otherwise required fields.
It returns $result, which indicates pass or failure, and the potentially modified
$user_data object
*/
$create = TRUE;
list($result, $user_data) = UserEntity::validateData($user_data, $create);

// equivalence allows you to pass back descriptive error messages
if ($result === TRUE) {
    // create the user in the database, get back $user_id...
    $user = new UserEntity($user_id, $user_data);
}
else {
    // return error to user
}

// access user data either individually, or if you want just make a getter
// for the entire group of data, so you can use it just like you would a
// separate UserData object
send_double_opt_in($user->getUserData());
?>

编辑以提供更多信息:

你说这些属性存在于UserEntity之外,并且它们可能自己存在......你的意思是这些属性可以被收集,使用和丢弃,甚至不用于UserEntity对象吗?如果是这种情况,则单独的对象将完全适合该数据。如果不是,如果数据总是从属于现有或未来的UserEntity,那么这些属性将永远不会自己生活在#34;来自......让我们把它称为全球数据&#34;观点看法。当您将整个系统视为一个整体,而不仅仅是代码时刻,那么数据&#34;可能属于&#34; UserEntity类。

对于静态方法,我认为没有特别的理由可以避免它们(显然),但是对于每个人来说都是如此。许多其他架构会稍微复杂一点,但这里有一些选择:

  1. 验证构造函数中的数据。问题是,如果它没有验证,您将不得不删除数据库条目。难看。
  2. 将数据库交互与数据验证一起移动到构造函数中。这可能违反了您的首选对象模型,并且您只需在创建对象后检查对象的状态(即设置公共属性$this->status = 'error';或类似的东西告诉您某些内容糟糕的事情,你必须处理)。
  3. 创建独立函数以验证数据。很丑,因为这是一个与UserEntity和/或其数据特别相关的功能。
  4. 或者,只需创建一个单独的UserData对象,就像您已建议并完成它一样。与第2步非常相似,您必须拥有某种$status属性来表示验证失败。

答案 1 :(得分:0)

对于一组可以独立于任何域对象使用的验证器来说,这似乎是一个很好的例子 - 也许这是我的函数式编程方面的问题,但是我没有看到创建一个问题的问题。一堆函数,如isValidEmail()

我知道你对一堆静态函数感到不舒服;你会考虑使用一堆方法来静态Validator类,以保持整洁吗?

无论哪种方式,您都可以在UserEntity对象之外使用此验证(我假设您正在讨论其他域对象,控制器中的输入验证等情况?)。但你也可以在'UserEntity'对象中使用它 - 对于我自己,我仍然没有确定在设置属性时是否更喜欢卫生处理和验证,或者当它被保存到持久存储时。我现在更倾向于通过第二个,通过setter函数。