CQRS - 如何处理查看页面或查询的用户
我正在将 CQRS 用于我正在构建的应用程序(一个具有复杂业务逻辑的在线讨论系统),并且我已经达到了让我担心的实现部分。
我应该如何处理浏览量?如果用户查看某个线程,我想对其进行跟踪。因为我想跟踪它,所以我应该创建一个命令和一个事件,并将其绑定到负责我正在查看的对象的聚合根(例如,分别为 UserViewsThread 和 UserViewedThread)。但这似乎效率很低——除此之外,没有理由在讨论系统的许多用例(查看论坛/线程)中达到聚合根。现在我要介绍这一点,我将在页面的每个视图上有一个额外的命令调度和事件发布,这反过来负责“假脱机”线程聚合,序列化我的事件,并将其发送到我的事件出版商。
必须有更好的方法来做到这一点。我正在考虑也许可以让我的控制器对象能够调度事件 - 但通过绕过我的聚合,我无法再将行为附加到页面视图。
另一种可能性是使用它作为对我的用户进行身份验证的方式。 UserViewsThread 和 UserViewsForum 命令将被允许抛出身份验证异常,让我的控制器知道用户无法执行此操作。但是,如果将这些事件转换为事件并存储在我的事件存储中,我们正在讨论在每个页面视图上创建的多个事件,这很可能会降低性能(每个页面视图都会导致数据库事务...呃)和资源管理。
你的男人有什么想法?
I'm using CQRS for an application that I'm building (an online discussion system with complex business logic) and I've reached a part in the implementation that worries me.
How should I handle pageviews? If a user views a thread, I want to track that. Because I want to track that, it follows that I should create a command and an event, and tie this into the aggregate root that is responsible for the object that I'm viewing (for example; UserViewsThread and UserViewedThread, respectively). But this seems widely inefficient - as other than this, there is no reason to hit an aggregate root in many of the use cases of a discussion system (viewing forums/threads). Now that I'm introducing this, I will have an additional command dispatch and event publish on every single view of a page, which is in turn responsible for "spooling up" the thread aggregate, serializing my event, and sending it to my event publisher.
There has to be a better way to do this. I was thinking of perhaps making it so that my controller object is capable of dispatching events - but by bypassing my aggregate, I can no longer attach behavior to a pageview.
Another possibility is the idea of using this as a way to authenticate my user. The UserViewsThread and UserViewsForum commands will be allowed to throw an authentication exception to let my controller know that the user cannot perform this action. But if these were to be translated into events, and stored in my event store, we're talking about multiple events created on every page view - which could very well murder performance (every page view results in a database transaction... ugh) and resource management.
What are your guy's thoughts?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用 CQRS 并不意味着与应用程序的每一次微小交互(尤其是与基础设施相关的交互)都必须传递命令/事件链。跟踪页面浏览量是一个跨领域的问题,可以轻松地单独实现。
只需让读取层中的查询在每次调用时为某个表中的相应视图模型增加一个计数器。
一旦您开始创建每次调用查询时都会改变状态的命令,您就会再次紧密耦合读取和写入,并且命令/查询分离的全部好处就会付诸东流。您首先使用 CQRS 来区分这两个问题。
Using CQRS doesn't mean that every tiny interaction with the application - especially if i's infrastructure-related - must be passing the command/event chain. Tracking pageviews is a cross-cutting concern that can easily be implemented seperately.
Just have the queries in the read layer increase a counter for the respective viewmodels in some table everytime they are called.
Once you start creating comands that mutate state every time a query is called you're tightly coupling read and write again and the whole benefit of Command/Query Separation goes down the drain. You're using CQRS to separate the two concerns in the first place.
您可以捕获会话中的跟踪信息并偶尔将其发送到服务器。
此外,为什么你认为你真的需要一个带有事件存储的 AR 来跟踪这些信息?我会将这些信息聚合到一些基于状态的存储中。
You can capture tracking information in a session and send it to the server once in a while.
Additionally, why do you think you really need to have an AR with event store to track this information? I would've aggregated this information in some state-based storage.
我们所做的几乎与此相同。但像这样跟踪所有事情感觉并不正确,您的读取应该很快并且不需要进行任何写入。
我们最终为所有事件添加了一个跟踪对象,但我认为我不会再这样做了。我会考虑编写一些 JavaScript,就像谷歌分析一样,并从前端创建一个单独的命令。
另外,我会将您的跟踪队列和正常队列分开。
We were doing almost the same as this. But it doesn't feel right to track everything like this, your reads should be fast and not required to do any writes.
We ended up adding a tracking object to all our events, but I don't think I would do that again. I would look into writing some javascript just like google analytics and create a separate command from the front-end.
Also I would separate out your tracking queue and your normal queue.
它似乎是一个网络应用程序,所以我创建一个属性(asp.net mvc/WebApi)来处理页面视图。当然,该属性只会调用基础设施服务(因为我认为跟踪统计数据不是域的一部分),该服务只会更新存储中的页面视图。简而言之,该服务调用一个更新持久性的 Dao(更新帖子集 pageviews=pageviews+1)。
当然,该属性可以只发送异步命令来更新页面视图,该命令将在后台执行,因此“写入”实际上不会干扰读取。
如果您的应用程序有/将有大量流量,那么我建议采用以下持久性策略:有一个表 PageViews(PageId,ViewsNumber,TimeStamp),您只需在其中为每个页面视图附加一行。然后每隔 X 分钟,有一个后台服务将执行 Sum(ViewsNumber) GroupBy(PageId) 其中 TimeStamp> 。旧时间戳。您不会获得毫秒级的统计数据,但它们会经常更新,并且应用程序将保持响应。
我不认为这个问题是您构建分析系统之外的任何领域的一部分,因此您不需要 AR、DDD 或事件溯源。对于您的情况,页面统计信息是附加到页面的信息,而不是页面的一部分。仅在读取模型中将它们一起考虑,您实际上在同一视图中显示它们。
It seems to be a web app so I'd create an attribute (asp.net mvc/WebApi) wich will handle the page views. Of course the attribute would only call an INFRASTRUCTURE service (as I think that tracking stats is not part of the domain) which will just update the page views in the storage. Simply put, the service calls a Dao which updates the persistence (update Posts set pageviews=pageviews+1).
Of course the attribute can just send async a command to update page views which will be executed in the background so the 'writes' won't actually interfere with the reads.
If your app has/will have lots of traffic then I'd suggest the following persistence strategy: Have a table PageViews(PageId,ViewsNumber,TimeStamp) where you just append a row for every page view. Then every X minutes, have a background service which will do a Sum(ViewsNumber) GroupBy(PageId) where TimeStamp> oldTimestamp. You won't have stats at the milisecond but they will be updated often enough and the app will stay responsive.
I don't consider this concern part of any domain outside the case you're building an Analytics system, so you don't need AR, DDD or Event sourcing. For your case, the page stats are information attached to the page and not part of a page. They are considered together only in the read model where you actually display them in the same view.