Oop 如何在SQL查询和应用程序逻辑(ColdFusion 8)之间集中业务规则?

Oop 如何在SQL查询和应用程序逻辑(ColdFusion 8)之间集中业务规则?,oop,coldfusion,coldfusion-8,business-rules,Oop,Coldfusion,Coldfusion 8,Business Rules,我一直在缓慢但肯定地对我客户的一个网站进行重新分解,以改进总体业务逻辑并避免重复代码。我一直在研究的主要领域之一是基本购物车应用程序中的产品: 对数据库使用SQL查询进行项搜索 产品详细信息模板 查看购物车 签出(输入地址、确认订单、提交) 围绕产品有许多业务规则,例如: 哪些产品是可查看的 某些用户可以查看哪些产品 哪些产品可以订购 每种产品可以以何种格式订购 等等 多年来,所有这些规则在整个网站上都是重复的——一些在SQL查询中,一些在应用程序逻辑中,一些在两者中——并且它们在某些页面上

我一直在缓慢但肯定地对我客户的一个网站进行重新分解,以改进总体业务逻辑并避免重复代码。我一直在研究的主要领域之一是基本购物车应用程序中的产品:

  • 对数据库使用SQL查询进行项搜索
  • 产品详细信息模板
  • 查看购物车
  • 签出(输入地址、确认订单、提交)
  • 围绕产品有许多业务规则,例如:

    • 哪些产品是可查看的
    • 某些用户可以查看哪些产品
    • 哪些产品可以订购
    • 每种产品可以以何种格式订购
    • 等等
    多年来,所有这些规则在整个网站上都是重复的——一些在SQL查询中,一些在应用程序逻辑中,一些在两者中——并且它们在某些页面上的实现方式是不同的。事实上,有些规则并没有在某些页面上实现(不是通过设计实现的)

    还创建了具有不同规则的新子应用程序

    因此,无论在哪里显示产品,我都实现了“product”类的使用,该类包含应用程序逻辑中的所有业务规则(即product.isViewable()、product.isOrderable()等)

    这允许通过扩展Product类在每个子应用程序中使用该应用程序的特定规则的产品数据

    我目前唯一的问题是itemsearch页面,它使用ColdFusion应用程序逻辑(if/else等)和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];