将 Trim 方法通常放在数据访问层还是领域层?

4
我正在处理一个包含数据不一致性的数据库,例如前导和尾随空格。通常情况下,我看到许多开发人员通过修剪几乎所有来自数据库的字符串来进行防御性编码,这些字符串可能在某个时候由用户输入。在我看来,最好在持久化数据之前进行这种格式化,以便只做一次,然后数据可以处于一致且可靠的状态。不幸的是,事实并非如此,这就引出了下一个最佳解决方案:使用 Trim 方法。
如果我将所有数据作为我的数据访问层的一部分修剪,则不必担心在域层的业务对象中进行防御性修剪。如果我将修剪责任放在我的业务对象中,例如用于设置 C# 属性的访问器,那么我应该会得到相同的净结果,但修剪将作用于分配给我的业务对象属性的所有值,而不仅仅是来自不一致数据库的值。
我想作为一个哲学上的问题,可能会决定答案,我可以问:“领域层应该负责数据的防御性/强制格式化吗?”在业务对象的PhoneNumber属性上设置访问器是否接受未格式化或格式化的字符串,并尝试根据需要进行格式化,还是应该将此责任推给表示层和数据访问层,使领域层更严格地接受数据类型?我认为这可能是更基本的问题。

更新:以下是我认为应该分享关于该主题的一些链接。

信息服务模式,第3部分:数据清理模式

LINQ to SQL - 在保存之前格式化字符串?

如何使用Linq to Sql修剪值?

3个回答

3
我建议在应用层“清洁”数据。你想要在这里(是的,像Dev Art建议的那样,在堆栈中更高的位置)进行操作的原因是你的领域模型应该尽可能地“模拟”领域。如果某个时刻所有数据都被“清洁”了呢?好吧,那么你可能想要删除执行“清洁”的辅助方法。从应用程序堆栈中更高的位置删除它会更容易。
使用一种优雅的扩展方法,利用反射(在你了解它如何工作之前不要告诉我反射很慢 ),或者其他方式来深入挖掘您的域对象图中的所有“字符串”属性(例如)。这里有一个使用此技术调整DateTime值的示例,请注意它将“偏移”所有DateTime值,即使是在集合或其他自定义类型中。在您的情况下,偏移将是您的修剪。这肯定比在整个项目中添加.Trim()更容易,并且可以相对容易地解耦。
记住,坏数据是与您的域相关的横切关注点,因此不应直接与其绑定(考虑AOP)。

2
数据在持久化之前必须进行清理。现在,您拥有不干净的数据,很可能需要在数据库中进行清理。考虑通过名称查找客户。如果我存储的是“ John”,“ Doe ”,我能否找到“John”,“Doe”?
在靠近用户界面的地方清理数据可以使代码更简单。防御性代码可以从清理代码变为断言(即assert string = trimmed(string))。要达到这一点,您需要清理数据库以及UI代码。

问题在于我们的数据库是两个独立应用程序之间的共同链接,其中只有一个应用程序我有控制权。因此,即使我集中精力纠正问题,坏数据仍会悄悄地进入。此外,以数据库为中心的清理操作将不足够,因为可能会在任何时刻插入格式不良的数据。另一个我无法控制的应用程序在代码中到处都是 Trim 和 Null/Empty 字符串检查,这是我想避免的。问题是如何最好地解决我几乎无法控制的格式不良的数据。 - jpierson
我非常赞同在领域层中,防御性代码能够转变为断言。我认为这就是为什么我开始考虑是否将我们业务对象中的一些现有验证代码更改为强制转换值而不是拒绝它们,这种微妙的变化是否会是不良实践或导致引入其他 UI 相关问题到错误的层中。 - jpierson
1
你可能需要构建触发器或代码来修剪或拒绝输入的数据。由于你无法控制应用程序,因此你需要处理它提供给你的垃圾数据。我更喜欢将这样的代码尽可能靠近边界放置,这样你就不必像处理应用程序那样在代码中到处散落。 - BillThor
我完全同意尽可能靠近边界放置代码的做法。我也考虑过触发器,我认为这可以作为一种合理的解决方案,配合某种类型的一次性清理脚本,在新软件更新期间运行。 - jpierson

0
最好在数据持久化之前进行这样的格式化。
当然。
以后域是否应该负责数据的防御性/强制性格式化?
使用当前存储的数据,您将找不到一个合适的地方来引入修剪。这是因为您的存储一致性已经被破坏了。
您可以尝试自我修复的方法。读取数据并在对话框中显示之前在某个地方修剪它。一旦用户保存此对话框,数据库中的数据就会“修复”。
至于新输入,我倾向于认为修剪数据是一种清理操作,既不属于域层也不属于数据层。在实际开始处理数据之前,用户输入应该在接近UI层的某个地方得到“清理”。

我的想法跟你差不多。不过我发现,把格式化代码放在领域模型里可以处理两个格式问题:一个是来自UI的数据,另一个是来自数据库的数据。这基本上就像你所描述的自愈方法,但不需要在UI中完成。但通常情况下,仅仅因为某事有一定的道理,并不意味着它就是正确的选择。 - jpierson
我还应该提一下,我正在使用LINQ to SQL,所以在我的情况下,在数据访问层中放置一些代码运行Trim()任何值是非常简单的,而且最好的是,这样做可以在数据被持久化时和从数据库中检索时格式化数据。显然,长期来看,两者都是过度的,但是现在,由于我必须处理糟糕的格式化数据,我认为这是一个合理的解决方案。另外,将代码放在我的业务对象中同样容易。哪个方法更正确?是否有其他适当的替代方案? - jpierson
1
就像我在我的答案中试图解释的那样:修剪不是应用程序域的问题,而是应用程序本身(服务于该域的应用程序)的问题。因此,在域/业务对象中不要修剪。在应用程序/服务层修剪,或者如果你真的必须这样做,在UI层修剪。但是再次强调,UI是用于呈现的,虽然这个责任似乎适合修剪,但实际上并不是。如果您没有应用程序或服务层,则UI应该是我认为的地方。 - Matt Kocaj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接