领域驱动设计布局问题

发布于 2024-08-05 13:32:13 字数 309 浏览 13 评论 0原文

我对 DDD 很陌生。我有一个配置文件类和一个配置文件存储库类。 PROFILE 类包含以下字段 -> Id、Description、ImageFilePath

因此,当我添加新的配置文件时,我将图像上传到服务器并将其路径存储在我的数据库中。

当我删除配置文件时,图像也应该从我的文件系统中删除。

我的问题:

我在哪里为此添加逻辑。我的配置文件存储库有一个删除方法。我应该在这里添加这个逻辑吗?或者我应该添加一个服务来封装这两个操作。

任何评论将不胜感激...

谢谢

Im new to the DDD thing. I have a PROFILE class and a PROFILE REPOSITORY CLASS.
The PROFILE class contains the following fields -> Id, Description, ImageFilePath

So when I add a new Profile, I upload then image to the server and store the path to it in my db.

When I delete the profile, the image should be removed from my file system aswell.

My Question:

Where do I add logic for this. My profile repository has a Delete method. Should I add this logic here. Or should I add a service to encapsulate both actions.

Any comment would be appreciated...

Thanks

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

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

发布评论

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

评论(3

策马西风 2024-08-12 13:32:13

您有两个与图像相关的不同“操作”。您有一个“物理”过程和一个“逻辑”过程。逻辑过程是将有关图像的信息保存到域存储库中,因为它是域的一部分。添加(和删除)的物理过程是逻辑过程的先决条件。

退一步说,物理过程完全独立于逻辑过程,但反之则不然。如果未保存图像,您显然不希望保留有关图像的元信息(在域中)。此外,如果无法删除物理文件,您也不想从域中删除信息。

该域应包含从数据源中删除图像的逻辑实例所需的信息。将域视为物理上独立的应用程序。在这种情况下,域实际上并不知道它所保存的数据与物理文件有任何关系。确保保持这种方式。

一般来说,我的实体位于一个程序集中,然后我的存储库和域服务位于另一个程序集中。应用程序服务位于域模型之外,但利用它来完成其工作。因此,应用程序服务使用一个或多个域服务或其他应用程序服务,并且域服务可以使用一个或多个存储库。

记住这一点,您有两个位置用于实际删除逻辑,第三个位置用于协调它们。如果我这样做的话,它会是这样工作的。域服务将利用存储库从底层数据源进行逻辑删除(以及您需要的检索)。除了使用域对象实例之外,它不知道任何其他事情。我还会有一个应用程序服务(域外),专门处理删除物理实例。为了便于论证,我假设您有一个“ImageRepository”类和一个“ImageServices”类,它们分别包含您的域存储库和域服务。您的 ImageServices 需要一个 Delete() 方法,以及您正在使用的任何 Find() 方法。我通常将查找方法显式调用为 FindBy...()(即 FindByKey()、FindByName() 等)。

如果您无法删除物理实例,则您不想删除逻辑实例,因此请确保您有一种方法来衡量物理映像删除操作是否成功。在这种情况下,我可能会采用某种自定义异常(因为我认为删除文件是一个标准操作,通常不会失败)。这通常属于“管理”领域。因此,通常我有一个名为“ImageManagementService”之类的应用程序服务。为了简单起见,该服务(因为它是应用程序的一部分而不是域)可以有一个私有方法来执行物理删除。我们称之为“DeleteImageFile()”。

第三个地方是这两个操作的协调,也是作为应用程序服务。我只是将其设为“ImageManagementService”中的公共方法。我们可以将其称为“RemoveImage”。该应用程序服务将执行以下操作:

  1. 从域服务检索实例信息(对存储库的直通调用)。
  2. 使用实例信息找到物理文件并将其删除(再次提到第一个应用程序服务)。
  3. 如果物理删除成功,则删除实例(返回域服务,再次外观存储库)。

因此,发生的情况是应用程序本身从“ImageManagementService”实例调用“RemoveImage()”方法。在内部,“RemoveImage()”首先从域的“ImageServices”调用“FindBy..()”以从域获取实例。文件路径用于调用“ImageManagementService”实例中的私有“DeleteImageFile()”方法。成功后,它将调用域的“ImageService”中的“Delete()”方法,该方法充当门面 我认为

在这种情况下专注于关注点分离非常重要,因为如果您有明确的分离(可以对不同的程序集执行此操作),您将很容易知道可以使用哪种逻辑。我强烈推荐 Evan 的书。此外,为了快速了解与 DDD 相关的 SOC 概念,我建议您看一下 Jeffrey Palermo 的“洋葱架构"。

仅说明一下为什么要使用域服务而不是调用存储库。主要是直接来自应用程序服务,存储库的实例比域服务更复杂。请记住,它主要是一个外观,但可能具有不适合域中其他任何地方的附加逻辑。一个很好的例子是,如果您想强制使用唯一的文件名。域对象本身不直接了解其他聚合中的其他域对象,因此域服务可能会在保存操作之前检查具有相同名称的现有实例。确实非常方便!此外,域服务不限于单个存储库。您可以让域服务协调多个存储库之间的工作。如果您有重叠的聚合,您可能需要同时调用两个相关聚合根的工作。您可以在域服务中执行此操作,将此类逻辑保留在域中并且不会渗透到应用程序中。

希望这有帮助。我确信还有其他方法可以做到这一点,但这是我在类似场景的我自己的应用程序中找到成功的方法。

You have two different "actions" related to the images. You have a "physical" process and a "logical" process. The logical process is persisting the information about the image into the domain repository, since it is part of the domain. The physical process of add (and delete) are a prerequisite to the logical process.

Taking a step back, the physical process is completely independent of the logical process, but the opposite is not true. You obviously do not want to persist meta-information about the image (in the domain) if the image was not saved. Also, you don't want to remove the information from the domain if you cannot remove the physical file.

The domain should contain the information required to remove the logical instance of the image from the datasource. Think of the domain as a physically separate application. In this case, the domain has no actual knowledge that the data it is persisting has anything to do with a physical file. Make sure to keep it this way.

Generally, I have my entities in an assembly, then my repositories and domain services in another. The application services live outside of the domain model, but leverage it to do its work. So application services use one or domain services or other application services and domain services can use one or more repositories.

Keeping this in mind, you have two places for the actual deletion logic, and a third place to coordinate them. Here is how it would work if I were doing it. The domain service will leverage the repository for the logical delete from the underlying datasource (as well as a retrieval which you will need, as well). It is not aware of anything else other than working with the domain object instance. I also would have an application service (outside of the domain) which specifically dealt with removing the physical instance. For argument sake, I will assume you have an "ImageRepository" class and an "ImageServices" class, which contain your domain repository and your domain services, respectively. Your ImageServices needs a Delete() method, as well as whatever Find() methods you are using. I usually explicitly call the find methods as FindBy...() (i.e, FindByKey(), FindByName(), etc.).

You don't want to remove the logical instance if you haven't been able to remove the physical instance, so make sure you have a means of measuring success of the removal operation for the physical image. I would probably go with some sort of a custom exception in this case (since I would consider deleting a file to be a standard operation that should not commonly fail). This usually falls in the realm of "management". So usually I have an application service named something like "ImageManagementService". For simplicity sake, this service (since it is part of the application and not the domain) can have a private method to do the physical delete. Let's call it "DeleteImageFile()".

The third place is a coordination of these two operations, also as an application service. I would just make this the public method in the "ImageManagementService". We can call this one "RemoveImage". This application service will do the following:

  1. Retrieve the instance information from the domain services (a passthrough call to your repository).
  2. Use the instance information to locate the physical file and remove it (the first application service mentioned, again).
  3. If the physical removal is successful, delete the instance (back to the domain service, facading the repository again).

So, what happens is the application itself calls the "RemoveImage()" method from the "ImageManagementService" instance. Internally, "RemoveImage()" first calls the "FindBy..()" from the domain's "ImageServices" to get an instance from the domain. The filepath is used from there to call to the private "DeleteImageFile() method in the "ImageManagementService" instance. Upon success, it will then call the "Delete()" methods in the domain's "ImageService", which is acting as a facade to your repository.

I think it is very important to focus on the separation of concerns in this case, because if you have an explicit separation (which you can do with different assemblies) you will become comfortable with knowing which kind of logic can go in which place. I highly recommend the Evan's book. Also, for a quick hit on the SOC concept as it relates to DDD, I recommend taking a look at Jeffrey Palermo's three part series on the "Onion Architecture".

Just a couple of notes as to why you would use a domain service instead of calling the repository directly from the application service. Primarily, the repository has more complicated instancing then the domain service. Remember, it is mostly a facade, but might have additional logic that does not fit in anywhere else in the domain. A good example of this might be if you wanted to enforce a unique filename. The domain object itself has no knowledge of other domain objects in other aggregates directly, so the domain service might check for an existing instance with the same name prior to a save operation. Very handy, indeed! Also, a domain service is not limited to a single repository. You can have a domain service coordinate efforts between multiple repositories. If you have overlapping aggregates, you might need to call work with two related aggregate roots at the same time. you can do this in the domain service, keeping that sort of logic in the domain and not bleeding into the application.

Hope this helps. I am sure that there are other ways to do this, but this is the way that I have found success in my own applications with similar scenarios.

节枝 2024-08-12 13:32:13

@joseph.ferris:“通常,我的实体位于一个程序集中,然后我的存储库和域服务位于另一个程序集中。”

就我个人而言,我更喜欢将程序集视为部署单元,而不是关注点分离设计工具。为此,我宁愿使用名称空间。

以这种方式确保(这些命名空间之间)没有循环依赖关系比较困难,但像 NDepend 这样的工具可以提供帮助。

@joseph.ferris: "Generally, I have my entities in an assembly, then my repositories and domain services in another. "

Personally, I prefer to see assemblies as a unit of deployment, not a separation of concerns design tool. For that, I'd rather use namespaces.

Ensuring no cyclic-dependencies (between those namespaces) that way is harder, but tools like NDepend can help out.

不及他 2024-08-12 13:32:13

对于第一种方法,我认为我会选择最简单的方法,并从 ImageRepository 内的磁盘中删除物理图像。
它可能不是最“正确”或“纯粹”的解决方案,但它是最简单的解决方案,这符合“选择最简单的可行解决方案”的慢板。

当在项目的后期阶段,您觉得这个解决方案不好,并且您觉得您需要一个更复杂(也许更纯粹)的解决方案(例如 joseph.ferris 提出的解决方案)时,您可以随时重构它。

重构一个简单的解决方案比重构一个复杂的解决方案更容易。 :)

On a first approach, I think I would opt for the most simple approach, and delete the physical image from disk inside the ImageRepository.
It is maybe not the most 'correct' or 'pure' solution, but it is the most simple one, and this conforms to the 'choose the most simple solution that works' adagio.

When, in a later phase of the project, you feel that this solution is not good, and you feel you need a more complex (and maybe more pure) solution like the one proposed by joseph.ferris, then you can always refactor it.

It is easier to refactor a simple solution, then to refactor a complex solution. :)

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