WPF/Silverlight 企业应用程序架构..你做什么?
我想知道社区里生活着什么已经有一段时间了; 我说的是大型的、面向业务的 WPF/Silverlight 企业应用程序。
理论上,有不同的模型在起作用:
- 数据模型(通常链接到数据库表、Edmx/NHibarnate/.. 映射实体)
- 业务模型(包含实际业务逻辑的类)
- 传输模型(暴露给外界的类(dto)) /client)
- 视图模型(您的实际视图绑定到的类)
很明显,这种分离具有明显的优势; 但它在现实生活中有效吗?这是维护的噩梦吗?
那么你做什么呢? 在实践中,您是否对所有这些模型使用不同的类模型? 我见过很多变体,例如:
- 数据模型 = 业务模型:数据模型首先实现代码(如 POCO 的),并用作带有业务逻辑的业务模型
- 业务模型 = 传输模型 = 视图模型:商业模式向客户公开;没有映射到 DTO,.. 发生。该视图直接绑定到此业务模型
- Silverlight RIA 服务,开箱即用,并公开数据模型:数据模型 = 业务模型 = 传输模型。有时甚至传输模型 = 视图模型。
- ..
我知道“这取决于”答案就在这里; 但这取决于什么? 您使用过哪些方法,您如何看待这些方法?
感谢分享,
问候, 科恩
I've been wondering what lives in the community for quite some time now;
I'm talking about big, business-oriented WPF/Silverlight enterprise applications.
Theoretically, there are different models at play:
- Data Model (typically, linked to your db tables, Edmx/NHibarnate/.. mapped entities)
- Business Model (classes containing actual business logic)
- Transfer Model (classes (dto's) exposed to the outside world/client)
- View Model (classes to which your actual views bind)
It's crystal clear that this separation has it's obvious advantages;
But does it work in real life? Is it a maintenance nightmare?
So what do you do?
In practice, do you use different class models for all of these models?
I've seen a lot of variations on this, for instance:
- Data Model = Business Model: Data Model implemented code first (as POCO's), and used as business model with business logic on it as well
- Business Model = Transfer Model = View Model: the business model is exposed as such to the client; No mapping to DTO's, .. takes place. The view binds directly to this Business Model
- Silverlight RIA Services, out of the box, with Data Model exposed: Data Model = Business Model = Transfer Model. And sometimes even Transfer Model = View Model.
- ..
I know that the "it depends" answer is in place here;
But on what does it depend then;
Which approaches have you used, and how do you look back on it?
Thanks for sharing,
Regards,
Koen
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
好问题。我从来没有编写过任何真正有进取心的东西,所以我的经验有限,但我会开始。
我当前的(WPF/WCF)项目使用数据模型=业务模型=传输模型=视图模型!
没有数据库后端,因此“数据模型”实际上是序列化为 XML 的业务对象。
我使用了 DTO,但很快发现整理工作极其困难,而且我内心一直存在的过早优化者不喜欢涉及不必要的复制。这可能会再次困扰我(例如,通信序列化有时与持久序列化有不同的需求),但到目前为止,这并不是什么大问题。
我的业务对象和视图对象都需要值更改的推送通知,因此使用相同的方法 (INotifyPropertyChanged) 实现它们是有意义的。这有一个很好的副作用,即我的业务对象可以直接绑定到 WPF 视图中,尽管使用 MVVM 意味着 ViewModel 可以在需要时轻松提供包装器。
到目前为止,我还没有遇到任何重大障碍,并且维护一组对象使事情变得美好而简单。我不敢想象如果我把所有四个“模型”分开,这个项目会有多大。
我当然可以看到拥有单独对象的好处,但对我来说,直到它真正成为一个问题之前,它似乎是浪费精力和复杂化。
正如我所说,这一切的规模都相当小,设计用于在几十台 PC 上运行。我有兴趣阅读真正的企业开发人员的其他回复。
Good question. I've never coded anything truly enterprisey so my experience is limited but I'll kick off.
My current (WPF/WCF) project uses Data Model = Business Model = Transfer Model = View Model!
There is no DB backend, so the "data model" is effectively business objects serialised to XML.
I played with DTO's but rapidly found the housekeeping arduous in the extreme, and the ever present premature optimiser in me disliked the unnecessary copying involved. This may yet come back to bite me (for instance comms serialisation sometimes has different needs than persistence serialisation), but so far it's not been much of a problem.
Both my business objects and view objects required push notification of value changes, so it made sense to implement them using the same method (INotifyPropertyChanged). This has the nice side effect that my business objects can be directly bound to within WPF views, although using MVVM means the ViewModel can easily provide wrappers if needs be.
So far I haven't hit any major snags, and having one set of objects to maintain keeps things nice and simple. I dread to think how big this project would be if I split out all four "models".
I can certainly see the benefits of having separate objects, but to me until it actually becomes a problem it seems wasted effort and complication.
As I said though, this is all fairly small scale, designed to run on a few 10's of PCs. I'd be interested to read other responses from genuine enterprise developers.
这种分离根本不是一场噩梦,事实上,自从使用 MVVM 作为设计模式以来,我非常支持它的使用。我最近是一个团队的一员,我使用 MVVM 编写了一个相当大的产品的 UI 组件,该组件与处理所有数据库调用等的服务器应用程序进行交互,可以诚实地说这是我从事过的最好的项目之一。
该项目有一个
我已将传输模型分类作为一件事,但实际上它是由多层构建的。
我还有一系列 ViewModel 类,它们是 Model 类的包装器,用于添加附加功能或更改数据的呈现方式。这些都支持 INPC,并且是我的 UI 绑定的。
我发现 MVVM 方法非常有用,而且老实说它使代码保持简单,每个视图都有一个相应的视图模型来处理该视图的业务逻辑,然后有各种底层类被视为模型。
我认为通过像这样分离代码可以让事情更容易理解,每个视图模型都不会有混乱的风险,因为它只包含与其视图相关的东西,我们在视图模型之间共有的任何东西都是通过继承来处理的减少重复代码。
当然,这样做的好处是代码立即变得更易于维护,因为对数据库的调用是在我的应用程序中通过对服务的调用来处理的,这意味着只要数据返回并且所需的参数保持不变,UI 永远不需要知道这一点。 UI 也是如此,没有代码隐藏的 UI 意味着可以很容易地调整 UI。
缺点是,遗憾的是,无论出于何种原因,你都必须在代码后面做一些事情,除非你真的想坚持使用 MVVM 并找出一些过于复杂的解决方案,所以在某些情况下,很难或不可能坚持真正的 MVVM 实现(在我们公司,我们认为这没有隐藏代码)。
总之,我认为,如果您正确地使用继承,坚持设计模式并强制执行编码标准,那么这种方法会非常有效,如果您开始偏离,那么事情就会开始变得混乱。
The seperation isnt a nightmare at all, infact since using MVVM as a design pattern I hugely support its use. I recently was part of a team where I wrote the UI component of a rather large product using MVVM which interacted with a server application that handled all the database calls etc and can honestly say it was one of the best projects I have worked on.
This project had a
I have classed the Transfer model as one thing but in reality it was built as several layers.
I also had a series of ViewModel classes that were wrappers around Model classes to either add additional functionality or to change the way the data was presented. These all supported INPC and were the ones that my UI bound to.
I found the MVVM approach very helpfull and in all honesty it kept the code simple, each view had a corresponding view model which handled the business logic for that view, then there were various underlying classes which would be considered the Model.
I think by seperating out the code like this it keeps things easier to understand, each View Model doesnt have the risk of being cluttered because it only contains things related to its view, anything we had that was common between the viewmodels was handled by inheritance to cut down on repeated code.
The benefit of this of course is that the code becomes instantly more maintainable, because the calls to the database was handled in my application by a call to a service it meant that the workings of the service method could be changed, as long as the data returned and the parameters required stay the same the UI never needs to know about this. The same goes for the UI, having the UI with no codebehind means the UI can be adjusted quite easily.
The disadvantage is that sadly some things you just have to do in code behind for whatever reason, unless you really want to stick to MVVM and figure some overcomplicated solution so in some situations it can be hard or impossible to stick to a true MVVM implementation(in our company we considered this to be no code behind).
In conclusion I think that if you make use of inheritance properly, stick to the design pattern and enforce coding standards this approach works very well, if you start to deviate however things start to get messy.
多层不会导致维护噩梦,而且层数越少,维护起来就越容易。我将尝试解释原因。
1) 传输模型不应与数据模型相同
例如,您的 ADO.Net 实体数据模型中有以下实体:
并且您希望从 WCF 服务返回它,因此您像这样编写代码:
还有一个问题:服务的使用者如何确保
Region
和Orders
属性不为 null 或空?如果Order
实体有一个OrderDetail
实体的集合,它们也会被序列化吗?有时您可能会忘记关闭延迟加载,整个对象图将被序列化。还有一些其他情况:
您需要组合两个实体并将它们作为单个对象返回。
您只想返回实体的一部分,例如
File
表中除二进制数组类型的FileContent
列之外的所有信息。您想要从表中添加或删除某些列,但您不想向现有应用程序公开新数据。
所以我认为您确信自动生成的实体不适合 Web 服务。
这就是为什么我们应该创建这样的传输模型:
并且您可以自由更改数据库中的表,而不影响 Web 服务的现有使用者,并且您可以更改服务模型而不更改数据库。
为了使模型转换过程更容易,您可以使用 AutoMapper 库。
2) 建议视图模型不应与传输模型相同。
虽然您可以将传输模型对象直接绑定到视图,但这只是一种“一次性”关系:模型不会反映在视图中,反之亦然。
大多数情况下,视图模型类会向模型类添加以下功能:
使用
INotifyPropertyChanged
接口通知属性更改有关使用
ObservableCollection
类的集合更改的通知验证属性
对视图事件的反应(使用命令或数据绑定和属性设置器的组合)
属性转换,类似于
{Binding Converter...}
,但在视图模型一侧同样,有时您需要组合多个模型以在单个视图中显示它们。最好不要依赖于服务对象,而是定义自己的属性,这样,如果模型的结构发生更改,视图模型将是相同的。
我总是使用上述 3 层来构建应用程序,并且效果很好,因此我建议每个人都使用相同的方法。
Several layers doesn't lead to maintenance nightmare, moreover the less layers you have - the easier to maintain them. And I'll try to explain why.
1) Transfer Model shouldn't be the same as Data Model
For example, you have the following entity in your ADO.Net Entity Data Model:
And you want to return it from a WCF service, so you write the code like this:
And there is the problem: how consumers of the service will be assured that the
Region
andOrders
properties are not null or empty? And if theOrder
entity has a collection ofOrderDetail
entities, will they be serialized too? And sometimes you can forget to switch off lazy loading and the entire object graph will be serialized.And some other situations:
you need to combine two entities and return them as a single object.
you want to return only a part of an entity, for example all information from a
File
table except the columnFileContent
of binary array type.you want to add or remove some columns from a table but you don't want to expose the new data to existing applications.
So I think you are convinced that auto generated entities are not suitable for web services.
That's why we should create a transfer model like this:
And you can freely change tables in the database without affecting existing consumers of the web service, as well as you can change service models without making changes in the database.
To make the process of models transformation easier, you can use the AutoMapper library.
2) It is recommended that View Model shouldn't be the same as Transfer Model
Although you can bind a transfer model object directly to a view, it will be only a "OneTime" relation: changes of a model will not be reflected in a view and vice versa.
A view model class in most cases adds the following features to a model class:
Notification about property changes using the
INotifyPropertyChanged
interfaceNotification about collection changes using the
ObservableCollection
classValidation of properties
Reaction to events of the view (using commands or the combination of data binding and property setters)
Conversion of properties, similar to
{Binding Converter...}
, but on the side of view modelAnd again, sometimes you will need to combine several models to display them in a single view. And it would be better not to be dependent on service objects but rather define own properties so that if the structure of the model is changed the view model will be the same.
I allways use the above described 3 layers for building applications and it works fine, so I recommend everyone to use the same approach.
我们使用的方法类似于 Purplegoldfish 发布的方法,但有一些额外的层。我们的应用程序主要与 Web 服务通信,因此我们的数据对象不绑定到任何特定的数据库。这意味着数据库架构更改不一定会影响 UI。
我们有一个由以下子层组成的用户界面层:
数据模型:这包括支持更改通知的纯数据对象。这些是专门用于 UI 的数据模型,因此我们可以灵活地设计它们以满足 UI 的需求。当然,其中一些对象并不简单,因为它们包含操纵其状态的逻辑。此外,由于我们使用大量数据网格,因此每个数据模型负责提供可绑定到网格的属性列表。
视图:我们的视图的 XAML 定义。为了满足一些复杂的要求,在某些情况下我们不得不诉诸代码隐藏,因为坚持仅使用 XAML 方法太乏味了。
ViewModels:这是我们为视图定义业务逻辑的地方。这些人还可以访问由下面描述的数据访问层中的实体实现的接口。
Module Presenter:这通常是负责初始化模块的类。它的任务还包括注册与该模块关联的视图和其他实体。
然后我们有一个数据访问层,其中包含以下内容:
传输对象:这些通常是由 Web 服务公开的数据实体。其中大部分是自动生成的。
数据适配器,例如 WCF 客户端代理和任何其他远程数据源的代理:这些代理通常实现一个或多个向 ViewModel 公开的接口,并负责异步对远程数据源进行所有调用,将所有响应转换为 UI根据需要等效数据模型。在某些情况下,我们使用 AutoMapper 进行翻译,但所有这些都专门在这一层完成。
我们的分层方法有点复杂,应用程序也是如此。它必须处理不同类型的数据源,包括 Web 服务、直接数据库访问和其他类型的数据源(例如 OGC Web 服务)。
We use an approach similar to what Purplegoldfish posted with a few extra layers. Our application communicates primarily with web services so our data objects are not bound to any specific database. This means that database schema changes do not necessarily have to affect the UI.
We have a user interface layer comprising of the following sub-layers:
Data Models: This includes plain data objects that support change notification. These are data models used exclusively on the UI so we have the flexibility of designing these to suit the needs of the UI. Or course some of these objects are not plain as they contain logic that manipulate their state. Also, because we use a lot of data grids, each data model is responsible for providing its list of properties that can be bound to a grid.
Views: Our XAML definitions of the views. To accommodate for some complex requirements we had to resort to code behind in certain cases as sticking to a XAML only approach was too tedious.
ViewModels: This is where we define business logic for our views. These guys also have access to interfaces that are implemented by entities in our data access layer described below.
Module Presenter: This is typically a class that is responsible for initializing a module. Its task also includes registering the views and other entities associated with this module.
Then we have a Data Access layer which contains the following:
Transfer Objects: These are usually data entities exposed by the webservices. Most of these are autogenerated.
Data Adapters such as WCF client proxies and proxies to any other remote data source: These proxies typically implement one or more interfaces exposed to the ViewModels and are responsible for making all calls to the remote data source asynchronously, translating all responses to UI equivalent data models as required. In some cases we use AutoMapper for translation but all of this is done exclusively in this layer.
Our layering approach is a little complex so is the application. It has to deal with different types of data sources including webservices, direct data base access and other types of data sources such as OGC webservices.