Oop 如何在SQL查询和应用程序逻辑(ColdFusion 8)之间集中业务规则?
我一直在缓慢但肯定地对我客户的一个网站进行重新分解,以改进总体业务逻辑并避免重复代码。我一直在研究的主要领域之一是基本购物车应用程序中的产品:Oop 如何在SQL查询和应用程序逻辑(ColdFusion 8)之间集中业务规则?,oop,coldfusion,coldfusion-8,business-rules,Oop,Coldfusion,Coldfusion 8,Business Rules,我一直在缓慢但肯定地对我客户的一个网站进行重新分解,以改进总体业务逻辑并避免重复代码。我一直在研究的主要领域之一是基本购物车应用程序中的产品: 对数据库使用SQL查询进行项搜索 产品详细信息模板 查看购物车 签出(输入地址、确认订单、提交) 围绕产品有许多业务规则,例如: 哪些产品是可查看的 某些用户可以查看哪些产品 哪些产品可以订购 每种产品可以以何种格式订购 等等 多年来,所有这些规则在整个网站上都是重复的——一些在SQL查询中,一些在应用程序逻辑中,一些在两者中——并且它们在某些页面上
- 哪些产品是可查看的
- 某些用户可以查看哪些产品
- 哪些产品可以订购
- 每种产品可以以何种格式订购
- 等等
WHERE (
LTRIM(RTRIM(UCASE(STATUS))) = 'ACTIVE'
OR UCASE(VIEWABLE_IF_RETIRED_FLAG) = 'Y'
)
<cfif SESSION.User.getSecurityLevel() LT 5>
AND (
UCASE(ORDERABLE_FLAG) = 'Y'
OR UCASE(ELECTRONIC_ORDERABLE_FLAG) = 'O'
OR UCASE(ELECTRONIC_ORDERABLE_FLAG) = 'V'
)
</cfif>
这让我发疯,因为它本质上仍然是重复的代码,如果业务规则发生变化,这两部分都需要管理
在某些地方,我只显示一些项目(例如,“类似项目”小部件以及“查看购物车”),我可以简单地从数据库中选择产品密钥,然后循环这些值来创建产品实例,并使用类中的业务规则来确定是否/如何显示产品
但是,item search查询最多可以返回2000条记录,而且我不能[实际上]循环2000条记录,创建2000个实例来根据业务规则确定显示
有什么想法吗?想一想:如果你要创建一个规则接口怎么办。即一套描述规则的CFC。如果它们都实现了相同的接口(无论是显式的还是通过duck类型),那么您可以修饰数据访问对象和其他类,例如产品类 这些规则的API可能类似(在伪代码中): 然后,您可以创建这些规则的数组,用于装饰您的对象:
rules = [isVisibleRule,isSomethingElseRule,etc];
在对象内部,您可以简单地循环数组并调用适当的函数来计算规则或生成正确的SQL片段。只有当函数或SQL语法的计算结果为true时,才满足该规则
总的来说,我不确定使用传统的SQL方法是否可以完全避免重复的业务逻辑。毕竟,它们都是(SQL和CF)不同的语言
我想另一种选择是定义一种特定于域的语言(DSL),并使用它来定义您的规则。然后编写一个组件,该组件可以将这些规则转换为SQL或根据对象对其进行评估。然后找到一种方法,将DSL中定义的特定规则以正确的方式关联到组件中,并根据需要对其进行评估
另一个挑战是,数据库似乎没有完全镜像对象模型,这是非常典型的。如果是这样的话,我敢肯定您只需要尽可能地隔离此逻辑。从这些业务规则的自动化测试(例如单元测试)开始。如果您听这些测试,它们将建议一个对象模型(例如,在编写测试时忽略当前的实现) 您可能会发现一些(可能几个)您希望存在的CFC来实现这些业务规则。编写测试,就好像它们存在一样 每个规则都应该有一个规范的实现。这就是说,如果您的规则确定哪些产品是可查看的,那么一个CFC方法应该确定这一点。该方法可以在内部使用CFML、SQL和对其他CFC方法的调用。关键是,需要使用该规则的所有内容都将调用该方法来执行该操作。您的测试将测试该方法,以查看是否正确遵循了规则 如果您还没有开始编写自动测试,请查看MXUnit并阅读TDD或BDD。
我的建议是在数据库设计中实现尽可能多的规则。请远离代码中存储的SQL,转到存储过程。在处理应该是数据库辅助项时,它有很大的帮助。@Limey我在这方面很有限,因为我们团队中只有一名数据库开发人员,而且他已经被高优先级的任务压得喘不过气来。另外,这仍然不能解决我在这里要问的问题。
component name="product is visible"{
function ruleAsSqlFragment(){
// return a chunk of SQL here that you can drop right into your query and would cause the query to be filtered as you wish.
return "LTRIM(RTRIM(UCASE(STATUS))) = 'ACTIVE'
OR UCASE(VIEWABLE_IF_RETIRED_FLAG) = 'Y'";
}
function validateRule(object){
// this function might accept an object, validate it according to its rules and return true or false
return object.isActive() or object.getViewableIfRetired()
}
}
rules = [isVisibleRule,isSomethingElseRule,etc];