design-patternsdomain-driven-designaggregateroot

How do you model an operation saving an image using DDD?


Basically, an user wants to change its profile picture. The web server receives a posted image against /user/35435/profile/picture, so the data needs to be saved, and the LastModification property of the profile object updated.

Images are not stored locally in the web server, they need to be uploaded somewhere else (ie: cloud storage).

At the moment, every operation is represented by either a Command or a Query. Commands run in an ambient transaction (like a SQL transaction). The image upload operation is not transactional, but a compensating action can be done in case of error (ie: removing the image if the DB operation fails).

On a naive implementation, a command containing both current date and the image data is created. A command handler is executed, it loads the ProfileAggregate aggregate, and ProfileAggregate.updateProfilePicture(imageUploader, image, currentDate) is executed passing the imageUploader domain service as parameter. Inside the method, the image is uploaded and the profile updated. The command handler saves changes in the DB and returns.

I do not like the fact that a transaction is held during the image upload. I do not like either doing non-domain-model operations (like uploading the image) from within an aggregate (even if the aggregate is calling somewhere else).

Should this interaction modeled like two independent commands that are executed in serial fashion, or it is OK to put anything inside an aggregate as long there is no dependencies in concrete implementations.


Solution

  • Your command handler is something that's probably in the "Application Layer" of your architecture. So, considering that, you receive a command and you orchestrate your domain / other services to satisfy it.

    That said, I don't really like two different commands implementation as from the client's point of view they're only doing one action. You could create a proxy command (single command) and on its handler generate two commands (one for handling the aggregate, the other to handle the upload) but this approach will make your API/Model less intuitive in the future.

    I also don't like bundling the upload part of the operation inside the Profile aggregate as it doesn't seem like one of its responsibilities at all.

    What I will suggest here is the following approach:

    As such, your command handler will:

    When ImageUploadService finished receiving the image, it will fire a message to the Profile domain reporting success/failure of the upload. Then: