如何处理域层和数据库层之间的通信?

发布于 2024-07-13 14:37:11 字数 447 浏览 8 评论 0原文

我对业务逻辑(域)和数据库访问逻辑使用单独的层相当陌生,但在解决问题的过程中,我遇到了一个问题,我仍然觉得我还没有找到很好的解决方案。

澄清我现有的解决方案使用数据映射器直接处理数据库交互。 然而,当我进一步研究这个问题时,许多人建议域层不应直接与实际执行数据库交互的数据映射器通信,也不应包含实际执行数据库交互的数据映射器。 这就是为什么我将存储库对象放置在域和必要的数据映射器之间,但这感觉不太自然或不正确。 因此,真正的问题是哪一层自然存在来处理域和数据映射器之间的通信? 任何有关如何构建它的示例将不胜感激。

例如:

  • 如何正确处理在另一个域对象的上下文中检索域对象的集合?
  • 如何根据对另一个对象执行的操作强制插入单个域对象或对象集合。 我当前面临的情况是,当一个人附加到一个营销活动时,我需要插入需要为该营销活动的该人执行的所有事件。

I am fairly new to using separate layers for the business logic (Domain) and database access logic, but in the course of working things out I've come across a problem to which I still feel I haven't found a great solution.

Clarification My existing solution uses Data Mappers to deal with the database interactions directly. However, as I've further investigated this issue many people have suggested that the Domain layer should not directly communicate with nor contain the Data Mappers that actually perform the database interaction. This is why I placed the Repository objects between the Domain and the necessary Data Mappers but this doesn't feel quite natural or correct. So the real question is what layer naturally exists to handle communication between the Domain and the Data Mappers? Any examples of how to structure it would be appreciated.

For example:

  • How do I properly handle retrieving a collection of domain objects within the context of another domain object?
  • How do I force the insertion of a single domain object or collection of objects based on an action performed against another object. The case I'm facing currently is that when a Person is attached to a Campaign, then I need to insert all of the Events that need to be executed for that Person for that Campaign.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鱼忆七猫命九 2024-07-20 14:37:11

领域模型和它的实现之间存在区别。 只是因为你的模型显示了一个关系 Person ---> 活动 ---> 事件并不意味着你必须以这种方式实现它。 IOW,您的模型以面向对象的方式显示您的分析和设计,但您在 OOP 中实现该模型,这在代码中复制该模型的能力方面受到限制。

考虑以下。

Person 不是由其对 Campaign 的所有权来定义的,因此 Campaign 可以被排除在其知识责任之外。 另一方面,Campaign 由作为其执行的一部分发生的Event 来定义,因此在一个campaign 中拥有一组事件是公平的。 我要表达的观点是,每个班级都应该有足够的行为和知识来使其完整。

至于域和持久层之间的通信,请将它们视为两个截然不同的系统,互不相关。 他们每个人只知道自己的职责是什么以及发布什么公告。 例如,持久层知道如何保存传递给它的数据并宣布数据已被保存。 然而,持久层不一定需要理解域对象。 同样,领域层理解 PersonCampaignEvent,但对持久性一无所知。

上述含义是,领域层本身需要是一个整体,并且其数据不应该依赖于持久层。 然而,它仍然需要提供数据来履行其职责。 该数据可以来自用户界面或数据库,并通过了解域层和持久层的第三方传递给它。

因此,在代码(伪 C#)中...

namespace DomainLayer
{
    interface IDomainListener 
    {
        void PersonCreated(Person person);
    }

    class Person
    {
        private string name;

        public Person(string name)
        {
            this.name = name;
        }

        public string Name
        {
            get { return name; }
        }
    }

    class Domain 
    {
        private IDomainListener listener;

        public Domain(IDomainListener listener) {
            this.listener = listener;
        }

        public void CreatePerson(string name) {
            Person person = new Person(name);
            listener.PersonCreated(person);
        }
    }
}


namespace PersistenceLayer
{
    interface IPersistenceListener
    {
        void PersonDataSaved(int id, object data);
    }

    class Persistence
    {
        private IPersistenceListener listener;

        public Persistence(IPersistenceListener listener) 
        {
            this.listener = listener;
        }

        public void SaveData(object data)
        {
            int id = ...; // save data and return identifier
            listener.DataSaved(id, data);
        }
    }
}

namespace MyApplication
{
    class MyController : IDomainListener, IPersistenceListener
    {
        public void CreatePersonButton_Clicked()
        {
            Domain domain = new Domain(this);
            domain.CreatePerson(NameTextbox.Text);
        }

        public void PersonCreated(Person person)
        {
            Persistence persistence = new Persistence(this);
            persistence.SavePersonData(person.Name);
        }

        public void DataSaved(int id, object data)
        {
            // display data on UI
        }
    }   
}

如您所见,命名空间代表不同的层。 XYZListener 接口定义由 XYZ 层发出的通知。 对这些公告感兴趣并对其做出响应的任何其他层都需要实现这些接口,我们的 MyApplication 层也是如此。

单击“创建按钮”时,控制器会为域层创建Domain外观对象,并将其自身注册为侦听器。 然后,它调用 CreatePerson 方法来实例化一个 Person,然后宣布此操作已完成,并传递新实例。 控制器在 PersonCreated 实现中响应此声明,在该实现中它生成持久层的外观并再次将自身注册为侦听器。 然后,它调用 SaveData 方法,该方法在完成时宣布 DataSaved。 然后,该方法的实现会在 UI 上显示数据。

正如您所看到的,领域层和持久层各自只知道自己,而不关心对方的职责。 应用程序逻辑(此处表现为控制器)将两者连接在一起。

回到您的具体问题,您可以在持久性上使用一个方法FindPerson,该方法将宣布PersonFound(int id)。 控制器的响应将是调用持久层来检索有关活动和事件的数据,然后使用该数据调用域层来构建Person

抱歉回答太长了...

There is a distinction between a domain model and the implementation of it. Just because your model shows a relationship Person ---> Campaign ---> Event does not mean that you have to implement it in this way. IOW, your model shows your analysis and design in an object-oriented way, yet you implement that model in OOP which is limited in how well it can replicate that model in code.

Consider the following.

A Person is not defined by its ownership of a Campaign, so campaign can be left out of its knowledge responsibities. On the other hand, a Campaign is defined by the Events that occur as part of its execution, so it is fair to have a collection of events within a campaign. The point that I am making is that each class should have just enough behaviour and knowledge to make it whole.

As for communication between the domain and the persistence layers, consider them as two very distinct systems that are not concerned with the other. All each of them knows is what its responsiblities are and what announcements it makes. For example, the persistence layer knows how to persist data passed to it and to announce that data have been saved. However, the persistence layer does not necessarily need to understand the domain objects. Similarly, the domain layer understands Person, Campaign, and Event but knows nothing about persistence.

The implication of the above is that the domain layer needs to be a whole by itself and should not be dependent on the persistence layer for its data. However, it still needs to be supplied with data to perform its responsibilities. That data can come from either the user interface or the database and is passed to it via a third-party that knows about both domain and persistence layers.

So, in code (pseudo-C#)...

namespace DomainLayer
{
    interface IDomainListener 
    {
        void PersonCreated(Person person);
    }

    class Person
    {
        private string name;

        public Person(string name)
        {
            this.name = name;
        }

        public string Name
        {
            get { return name; }
        }
    }

    class Domain 
    {
        private IDomainListener listener;

        public Domain(IDomainListener listener) {
            this.listener = listener;
        }

        public void CreatePerson(string name) {
            Person person = new Person(name);
            listener.PersonCreated(person);
        }
    }
}


namespace PersistenceLayer
{
    interface IPersistenceListener
    {
        void PersonDataSaved(int id, object data);
    }

    class Persistence
    {
        private IPersistenceListener listener;

        public Persistence(IPersistenceListener listener) 
        {
            this.listener = listener;
        }

        public void SaveData(object data)
        {
            int id = ...; // save data and return identifier
            listener.DataSaved(id, data);
        }
    }
}

namespace MyApplication
{
    class MyController : IDomainListener, IPersistenceListener
    {
        public void CreatePersonButton_Clicked()
        {
            Domain domain = new Domain(this);
            domain.CreatePerson(NameTextbox.Text);
        }

        public void PersonCreated(Person person)
        {
            Persistence persistence = new Persistence(this);
            persistence.SavePersonData(person.Name);
        }

        public void DataSaved(int id, object data)
        {
            // display data on UI
        }
    }   
}

As you can see, the namespaces represent the different tiers. The XYZListener interfaces define the announcements that are made by the XYZ tier. Any other tiers that are interested in these announcements and will respond to them need to implement these interfaces, as does our MyApplication tier.

When the "create button" is clicked, the controller creates the Domain facade object for the domain layer and registers itself as a listener. It then calls the CreatePerson method which instantiates a Person then announces that this has been done, passing the new instance. The controller responds to this announcement in the PersonCreated implementation where it spawns a facade of the persistence layer and registers itself as the listener again. It then calls the SaveData method whichannounces DataSaved when completed. The implementation of that method then displays the data on the UI.

As you can see, the domain layer and the persistence layer are each aware of only tmemselves and are not concerned with the responsibilities of the other. It is the application logic, manifested here as the controller, that wires the two together.

Back to your specific problem, you could have a method FindPerson on the persistence, which would announce PersonFound(int id). The response by the controller would be to call the persistence layer to retrieve data about campaign and events, then call the domain layer with that data to build the Person.

Sorry for the long answer...

满栀 2024-07-20 14:37:11

Gabriel,这称为“阻抗匹配问题”。 有许多解决方案,从 J2EE 实体 bean 等重量级解决方案到 Ruby ActiveRecord,再到简单地编写手动连接。

更新

好吧,如果没有更多信息,很难确切地了解如何攻击这个问题,但这是基本方法。

任何此类架构问题都是由非功能性需求(例如性能)驱动的; 此外,这里还存在一个正确性问题,因为您需要确保更新按照正确的顺序完成。 因此,您需要考虑工作负载,即实际应用程序中的使用模式。 考虑到这一点,您基本上会遇到几个问题:首先,应用程序中的基本数据类型可能无法正确映射到数据库(例如,代码中表示的 VARCHAR 属性是什么?),其次是您的域模型可能无法清晰地映射到您的数据库模型。

您希望数据库和域模型能够正常工作,以便域对象的一个​​实例恰好是数据库模型中表的一行; 在大型应用程序中,由于性能限制或预先存在的数据库模型所施加的限制,您很少可以这样做。

现在,如果您完全控制您的数据库模型,它会在一定程度上简化事情,因为这样您就可以使您的数据库模型更加类似于域。 这可能意味着数据库模型在某种程度上是非规范化的,但如果是这样,您可以(取决于您的数据库)使用视图来处理它,或者只是没有完全规范化的数据库。 归一化是一个有用的理论构造,但这并不意味着您不能在实际系统中放松它。

如果您完全控制数据库模型,那么您需要一层对象来进行映射。 在实现过程中,您有很多选项可供选择:您可以在数据库中构建视图或非规范化表,可以构建中间对象,或者可以两者都做一些,甚至可以同时执行两个步骤(即,访问非规范化表的中间对象。)

但是,此时您会遇到“不要重复自己”和“做可能有效的最简单的事情”的问题。 想想什么最有可能改变? 你的领域模型? 如果您有一个强大的领域模型,那么这种情况就不太可能发生——业务变化相对很少。 数据库中数据的准确表示? 比较常见一点。 或者,最常见的是,确切的使用模式(例如发现需要处理并发更新)。因此,当您考虑这一点时,您需要做什么才能尽可能轻松地处理最常见的更改。

我意识到这并没有给您提供非常精确的说明,但我认为在不了解您的应用程序的全部情况下我们无法提供精确的说明。 但我也有一种印象,你想知道处理这个问题的“正确”方法是什么,而你已经在处理或多或少能完成工作的东西。 所以,我最后会问“你现在对什么不满意?” 和“你想如何解决这个问题?”

Gabriel, this is called the "impedance matching problem." There are many solutions around, from heavyweight ones like J2EE entity beans to Ruby ActiveRecord to simply coding a hand connection.

Update

Okay, well, its hard to see exactly how to attack this without a lot more information, but here's the basic approach.

Any of these sorts of architectural issues are driven by non-functional requirements like performance; in addition, there is a correctness issue here, in that you want to make sure updates are done in the correct order. So, you're going to need to think about the workload, which is to say the pattern of usage in real-world application. With that in mind, you basically have a couple of issues: first, the base data types in your application may not map correctly to the data base (eg, what's a VARCHAR property represented as in your code?), and second your domain model may not map cleanly to your database model.

What you would like is to have the database and the dmain model work out so that one instance of a domain object is exactly a row of a table in your database model; in large-scale applications you can rarely do this because of either performance constraints or constraints imposed by a pre-existing database model.

Now, if you completely control your database model, it simplifies things somewhat, because then you can make your database model more closely resemble the domain. This might mean the database model is somewhat denormalized, but if so, you can (depending on your database) handle that with views, or just not have a completely normalized database. Normalization is a useful theoretical construct, but that doesn't mean you can't relax it in a real system.

If you don't completely control your database model, then you need a layer of objects that make the mapping. You've got a bunch of options to choose from in implementing that: you can build views or denormalized tables in the database, you can build intermediate objects, or you can do some of both, or even have several steps of both (ie, an intermediate object that accesses a denormalizaed table.)

At that point, though, you run into issues with "don't repeat yourself" and "do the simplest thing that will possibly work." Think about what is most likely to change? Your domain model? If you've got a strong domain model, that's less likely --- the business changes relatively rarely. The exact representation of data in the database? A little more common. Or, most commonly, the exact patterns of use (like discovering a need to handle concurrent updates.) So, when you think about that, what do you need to do to make it as easy as possible to deal with the most common changes.

I realize this isn't giving you very precise instructions, but I don't think we can offer precise instructions without knowing a whole lot about your applicaiton. But then I also kind of get the impression you're wondering about what the "right" way of handling this would be, while you are already working with something that more or less does the job. So, I'd end up by asking "what are you unhappy with now?" and "How would you like to solve that?"

近箐 2024-07-20 14:37:11

许多系统采用独立的数据层来处理数据库的持久性。 此类层的组织有多种模型。 有些使用一种类似工厂的实现,其他则采用一对一映射,每个域类有一个数据层类。

数据层的模型通常取决于风格和偏好。 重要的是将持久层与域层分开。 我相信有一些工具可以帮助您生成这一层,但我的 PHP 知识很薄弱,所以我无法专门为 PHP 命名。

Many systems employ an independent data layer to handle persistence to and from a database. There are several models for the organization of such a layer. Some use a sort of factory-like implementation, others employ a one-to-one mapping with one data layer class per domain class.

The model for the data layer often depends on style and preference. What is important is separating the persistence layer from the domain layer. I believe there are tools out there that will help you generate this layer, but my PHP knowledge is thin so I can't name any specifically for PHP.

2024-07-20 14:37:11

我会看看 PHPCake 和 Symfony 使用的数据抽象层。

I would look at the data abstraction layers used by PHPCake and Symfony.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文