3

My domain requires that a Resource (ubiquitous language for a singular unit of data to be moved - commonly a file or folder) can only be moved if it has been Scanned to ensure it does not contain any Threats. Resources are moved from a Lowside MountedDrive (essentially some external filesystem, as far as I know a lowside is just the hostname of a machine, and the mounted drive contains base path information) to 1 or more Highside data repos. The entity that encapsulates this is called an Ingest

Implementation will involve the usage of multiple antivirus softwares and a custom written content check. It is obvious that implementation of the adapters for the antivirus software must be in the infrastructure layer. I am not so certain for the custom written content check though.

My learning so far indicates to me that the process of a scan does not belong to a domain entity, so it is a domain concern. So I believe this is a prime example of a domain service. These scans may take a long time, far too long to be waited on synchronously.

  1. how can I design my IResourceScanner to be run asynchronously without being a leaky abstraction?
  2. Should this Scanner scan the Ingest that contains the resources that need to be scanned or be independent of the Ingest
public interface IResourceScannerService {
    void StartScan(Resource r, Lowside l, MountedDrive m);
}

// or

public interface IIngestScannerService {
    void StartScan(Ingest i);
}

Neither doesn't seem to tell what it does from the interface. In addition, it would be hard to know that you need to update properties on the resource and persist them for it to be recognized as scanned. Perhaps this is a use case for domain events but I am honestly unsure. You also cannot cancel a running scan with this. This entire thing eludes me.

It's hard to know how to use this without knowing how to run it as a background process that is non-blocking. There are 2 possible things that make sense to me;

// Directly in the entity
class Ingest : ... {
    public void ScanResources(IResourceScannerService scanner) {
        // Queues all the scans for its resources.
    }
}

// In a use case
class ScanIngestUseCase {
    private IIngestScannerService scanner

    public ScanIngestUseCase(IIngestScannerService s) {
        scanner = s
    }

    void execute(int ingest_id) {
        ingest = // get the ingest from repo
        s.StartScan(ingest)
    }
}

In addition, what would be the natural way to actually access and manipulate the Resource, given we only have a Lowside and MountedDrive? I'm thinking that accessing the resources is not a domain issue - it is an implementation detail of the scanning and transfer logic which exists in the domain layer. Thus perhaps a simple TerminalClient factory like;

public class TerminalClientFactory {
    ITerminalClient create(string connection_string) {
        ...
    }
}

that exists purely in the infrastructure layer would be appropriate. It is a requirement that we have a commandline of sorts to operate on. This raises the question; how do I handle all the kinds of terminals? My requirements luckily only require I support Ubuntu, but for curiousity sake what if I wanted to also support Windows, Centos, etc?

1

1 Answer 1

2

My learning so far indicates to me that the process of a scan does not belong to a domain entity, so it is a domain concern. So I believe this is a prime example of a domain service.

Perhaps this is a use case for domain events but I am honestly unsure.

It is obvious that implementation of the adapters for the antivirus software must be in the infrastructure layer.

You are not in the right mindset yet for design, judging by the above, if you'll excuse my presumptuousness.

What you're thinking about are technical tools you want to use. How do I use this-and-that to do things. It's backwards. First look at what you want to express. In the framework of DDD, concentrate on the ubiquitous language, the domain language. Try to express as much knowledge as you can, starting from the most important ones.

For example, it is important, that stuff is not moved before scanned. Ok, assuming I'll have different Resource types, I would express that as (keep in mind this is an example, may not fit all requirements you have):

interface Resource {
   void moveTo(target);
}

interface Unscanned<T> {
   T scan();
}

So any time I see Unscanned<Resource> as type, it tells me that the resource is not scanned, and I can't actually invoke moveTo() unless I scan first.

Anyone who reads that will know: Right, I can't move this thing until I scan it, got it.

"Ingest" is the term for the "factory" that produces the resources, right? That is basically the "repository" in DDD terminology where my initial objects come from? Well then:

public final class Ingest {
   public Unscanned<Resource> readResources() { ... }
}

Those are just a couple of ideas to demonstrate that the language comes first. All the class names and method names must make sense. Whether any of those are "entities", "repositories" or "services" or whatever is a detail. It's not important and not even visible, may even change during development as you try to express more in all the constraints and requirements you were given.

What I'm saying is, try to step back, forget for a moment all the DDD stuff. Try to tell me (reader) as much as you can about your domain with your design as you possibly, reasonably can. Try to push as much as you can from your free-form text about the requirements to code. And also, try to not waste my time with details. Put important, domain-relevant stuff into it, instead of telling me something is a "service".

Does that make sense?

1
  • Thank you for the answer! You raise interesting points and prove that I still have a lot to learn :p. I won't lie, I find myself missing probably a significant amount of useful information from the answer that I hope I can look back and recognize in the future. One thing, "Ingest" is more like the aggregate root I believe. When my domain experts talk about doing this process, they say "we have an ingest to do" - it is the ingests resources that are scanned, the ingest has a lowside, etc. It is the encapsulating idea for one of the processes I am trying to model. Aug 25, 2022 at 10:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.