I opted to refer to "modules" instead of "layers" here, because it fits a more general approach for many different architectures and I wanted to keep the name consistent.
There are different approaches here, depending on how your architecture is set up.
- You can go for a modular design, where each bounded context exposes its own interface.
- You can centralize your interfaces, and consider your bounded contexts as the implementation of the interface that was defined in that central location. Whether you bundle all your interfaces in one centralized location, or create a separate interface bundle per bounded context, is up to you to decide.
- When working with inverted dependencies, it is the consumer who defines the interfaces, and the module only acts as the implementer of said interface.
Note that the last two bullet point focus on public interfaces (i.e. between modules), not internal interfaces (i.e. only used inside the module itself).
As you're working in node.js, it's very easy to start putting interfaces where you please, as node.js is quite unconstrained about its dependency graph. I'm a .NET dev, and .NET enforces a rigid project dependency graph, which in turn very much dictates where you can and can't put your interfaces so that the implementations can access them.
In your case, you have ample freedom to choose how you structure things. The good thing is that you get choice, the bad thing is that it requires self discipline to stick to your choice and remain consistent.
Which of these you use is up to you to decide, based on what makes the most sense in your architecture and development approach.
Possible considerations:
- If you're dealing with a central domain module which is the meat of your application and which calls the shots, inverted dependencies make a lot of sense as the domain will be the one demanding what the outer layers must provide for the domain. Render unto Caesar the things that are Caesar's.
- When there are multiple consumers, inverted dependencies make little sense because these consumers couldn't easily coordinate on using the same interface.
- If your modules independently decide what they provide (which often happends when separate teams develop them), it makes more sense to have the moduels themselves define their public interface. My house, my rules.
- If you might end up with variant implementations and you choose to separate them into individual modules (e.g. a MongoDb DAL module, a file storage module, an in-memory storage module, ...) as opposed to a single module with (internally) multiple possible implementations; it makes more sense to define the DAL interfaces separately, so that all of these modules have access to it without one specific module claiming ownership over them.
- If you intend to distribute your application as packages, it makes a lot of sense to create such individual modules so consumers only download the modules that they need.
These are just some of the possible considerations that can influence which approach best fits your use case.
In general, the rule is that interfaces belong with the party that decides the interface's structure. Different development methodologies put that responsibility in different locations.