- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一个用 Zend Framework 编写的 MVC 应用程序,它从 Oracle 10g 数据库中提取数据并将这些数据显示在表格和列表中,并通过颜色和图表直观地丰富这些数据。没有 ORM,也没有涉及创建、更新或删除,只是纯粹的阅读。数据是从另一个应用程序插入的。数据库中的数据是根据它们所代表的概念建模的,并由数据库 View 访问,这些 View 从各种其他表(遗留的,无法更改的)聚合这些数据,例如
| Event ID | Start | End | Status | Created_By |
-----------------------------------------------------------------------------
| 12345678 | 2009-10-01 12:00:00 | 2009-10-01 12:15:00 | booked | John Doe |
| 12345679 | 2009-11-01 13:00:00 | 2009-12-01 12:00:00 | booked | John Doe |
| 12345680 | 2009-11-01 13:00:00 | 2009-12-01 12:00:00 | tba | Jane Doe |
用户可以从 View 中影响列显示、排序和排序。客户端可以拒绝/允许访问列并将列内容限制为特定值。用户无法覆盖客户端设置。用户是一个参与者,而客户端基本上只是一个过滤器,它为属于客户端的用户创建可用数据的子集。保留用户和客户端设置。
我目前的做法大致是这样的:
Request --> Controller
| <--> sanitizes and returns Request params
| ---> Facade (capsules steps to fetch View Data)
| | <--> Table Data Gateway builds Query for requested View
| | <--> Query Decorator¹ applies User/Client settings
| | <--> DB Adapter fetches RecordSet from decorated Query
| <----returns Recordset
| <--> applies RecordSet to View
| <--> Data-Aware ViewHelper render RecordSet (and View)
Response <--returns rendered View
¹
查询装饰器可以读取持久化的用户/客户端设置,并将其添加到 TDG 动态返回的基本查询对象中。
但是,最近我一直怀疑这种方法并想改进它。我想我可以完全删除 TDG,并从 UI 中构建完全通用的 View ;仅基于数据库结构。用户肯定会喜欢这个。问题是,View 必须了解很多关于数据的信息。 ViewHelpers 必须知道列名才能丰富数据,而且它们通常对 Recordsets 中的多个列这样做。它们不能是通用的,有些东西告诉我这无论如何都是麻烦的。感觉就像大杂烩。我只是无法确定原因。
非常感谢任何模式、想法和意见。我知道这个问题有些含糊,但正如我所说,我无法确定是什么让我怀疑这种方法。所以我想我正在寻找任何好的实践方法来以可维护的方式构建用户和客户端可定制的以数据库为中心的应用程序。我当然不需要解决方案,只需要一些想法和一些链接,看看其他人是如何解决这个问题的,这样我就可以在下一次重构时考虑到它。
注意
在接受答案之前,我会将问题一直悬而未决。感谢任何输入。
最佳答案
在重新阅读你的问题几次并稍微反射(reflection)之后,我相信我会这样总结情况:
您的“MVC”中缺少“M”。
此时,您已经手工制作了一个关系数据库架构,使其与您的领域模型具有 1:1 的映射关系。这很好,它使映射变得非常容易,但记录集仍然不是域类。
MVC 上下文中的术语模型 指的是域模型,而不是关系模型。如果你有一个关系数据库支持这个应用程序,那么你需要某种映射。这并不是说您需要像 Doctrine 这样的成熟的 ORM 框架——尽管我确实发现这些工具让我的生活变得更轻松,即使对于小型项目也是如此——但你需要一些东西。事实上,Zend Framework 甚至在 Quick Start 中详细介绍了映射领域模型的细节。 .
我认为您不需要删除 TDG。抽象是好的。拆掉它以使您的应用程序更精简一点,我将其比作进入办公大楼并拆掉电话系统,理由是员工只能使用他们的手机。它们可以,但您不希望它们这样做,就像您不希望您的 View 直接向数据库抛出 SQL 查询一样。它效率低下且通常难以管理。
我的架构版本如下所示:
Request --> Controller
| <--> sanitizes and returns Request params
| ---> Facade (encapsulates steps to fetch View Data)
| | <--> Table Data Gateway builds Query for requested View
| | <--> Query Decorator applies User/Client settings
| | <--> DB Adapter fetches RecordSet from decorated Query
*** | | <--> Mapping layer converts RecordSet to Domain Model
*** | <----returns Model
*** | <--> applies Model to View
*** | <--> Data-Aware ViewHelper render Model (and View)
Response <--returns rendered View
我用 ***
标记了更改行。实际上,我唯一改变的是,它不是从外观中获取记录集,而是获取模型(可能是域类数组),并将 那个 应用到 View 。
您的 View [Helper] 中没有像 $row['Status']
这样的术语,您将拥有 $event->status
,这更安全、更简单以长期维持。那里没有列名,只有一个属性。
现在您在问题的最顶部明确提到您没有任何 ORM,所以我认为您可能已经了解其中的大部分内容,并且可能只需要插入一下。您脑子里那些挥之不去的疑虑可能是这样的:如果它不总是只读的怎么办?如果数据模型变得更复杂怎么办?如果人们开始要求更复杂的报告怎么办?
所有这些都是你拥有领域模型的原因,为什么它实际上是 MVC 的基本构建 block :最终,你的用户拥有的心智模型将与数据模型,出于多种原因,我不会在这里讨论。关键是,它几乎总是会发生。
我确定这是必要的吗?我是否肯定这不仅仅是矫枉过正,一堆对这么小的项目毫无意义的仪式咒语?
不,我不是。只有你能决定。我可以告诉您的是,如果没有适当的域模型,作为特定 体系结构的 MVC 范例对您没有多大用处。它比在每个页面中仅进行内联查询或外观调用要好一些,但那也好不了多少。如果没有模型,MVC 只不过是一种奇特的 URL 重写方案。
也许您需要这种抽象级别,也许您不需要;但我猜你可能怀疑你可能会,否则你不会问这个问题。想一想,分析当前的需求和范围,问问自己可能发生什么样的变化,如果当前的架构看起来太脆弱而无法适应,那么下一个合乎逻辑的步骤将是领域模型——即使今天 它只是关系模型的精确镜像。明天可能就不是了。
希望这就是您正在寻找的答案!
关于php - 您将如何为这个应用程序建模?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2199909/
我正在通过 labrepl 工作,我看到了一些遵循此模式的代码: ;; Pattern (apply #(apply f %&) coll) ;; Concrete example user=> (a
我从未向应用商店提交过应用,但我会在不久的将来提交。 到目前为止,我对为 iPhone 而非 iPad 进行设计感到很自在。 我了解,通过将通用PAID 应用放到应用商店,客户只需支付一次就可以同时使
我有一个应用程序,它使用不同的 Facebook 应用程序(2 个不同的 AppID)在 Facebook 上发布并显示它是“通过 iPhone”/“通过 iPad”。 当 Facebook 应用程序
我有一个要求,我们必须通过将网站源文件保存在本地 iOS 应用程序中来在 iOS 应用程序 Webview 中运行网站。 Angular 需要服务器来运行应用程序,但由于我们将文件保存在本地,我们无法
所以我有一个单页客户端应用程序。 正常流程: 应用程序 -> OAuth2 服务器 -> 应用程序 我们有自己的 OAuth2 服务器,因此人们可以登录应用程序并获取与用户实体关联的 access_t
假设我有一个安装在用户设备上的 Android 应用程序 A,我的应用程序有一个 AppWidget,我们可以让其他 Android 开发人员在其中以每次安装成本为基础发布他们的应用程序推广广告。因此
Secrets of the JavaScript Ninja中有一个例子它提供了以下代码来绕过 JavaScript 的 Math.min() 函数,该函数需要一个可变长度列表。 Example:
当我分别将数组和对象传递给 function.apply() 时,我得到 NaN 的 o/p,但是当我传递对象和数组时,我得到一个数字。为什么会发生这种情况? 由于数组也被视为对象,为什么我无法使用它
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章ASP转换格林威治时间函数DateDiff()应用由作者收集整理,如果你
我正在将列表传递给 map并且想要返回一个带有合并名称的 data.frame 对象。 例如: library(tidyverse) library(broom) mtcars %>% spl
我有一个非常基本的问题,但我不知道如何实现它:我有一个返回数据框,其中每个工具的返回值是按行排列的: tmp<-as.data.frame(t(data.frame(a=rnorm(250,0,1)
我正在使用我的 FB 应用创建群组并邀请用户加入我的应用群组,第一次一切正常。当我尝试创建另一个组时,出现以下错误: {"(OAuthException - #4009) (#4009) 在有更多用户
我们正在开发一款类似于“会说话的本”应用程序的 child 应用程序。它包含大量用于交互式动画的 JPEG 图像序列。 问题是动画在 iPad Air 上播放正常,但在 iPad 2 上播放缓慢或滞后
我关注 clojure 一段时间了,它的一些功能非常令人兴奋(持久数据结构、函数式方法、不可变状态)。然而,由于我仍在学习,我想了解如何在实际场景中应用,证明其好处,然后演化并应用于更复杂的问题。即,
我开发了一个仅使用挪威语的应用程序。该应用程序不使用本地化,因为它应该仅以一种语言(挪威语)显示。但是,我已在 Info.plist 文件中将“本地化 native 开发区域”设置为“no”。我还使用
读完 Anthony's response 后上a style-related parser question ,我试图说服自己编写单体解析器仍然可以相当紧凑。 所以而不是 reference ::
multicore 库中是否有类似 sapply 的东西?还是我必须 unlist(mclapply(..)) 才能实现这一点? 如果它不存在:推理是什么? 提前致谢,如果这是一个愚蠢的问题,我们深表
我喜欢在窗口中弹出结果,以便更容易查看和查找(例如,它们不会随着控制台继续滚动而丢失)。一种方法是使用 sink() 和 file.show()。例如: y <- rnorm(100); x <- r
我有一个如下所示的 spring mvc Controller @RequestMapping(value="/new", method=RequestMethod.POST) public Stri
我正在阅读 StructureMap关于依赖注入(inject),首先有两部分初始化映射,具体类类型的接口(interface),另一部分只是实例化(请求实例)。 第一部分需要配置和设置,这是在 Bo
我是一名优秀的程序员,十分优秀!