Code Monkey home page Code Monkey logo

Comments (9)

borjapazr avatar borjapazr commented on May 20, 2024

Hello, @robertgr991 👋!

First of all, thank you very much for opening this issue and contributing to this project.

The truth is that I agree with you. I had been wanting to implement Vertical Slice Architecture in this project for some time, but due to lack of time, I never took the step. I believe that, together with DDD principles and a Hexagonal/Clean Architecture, you can create projects that scale very well and, most importantly, that are maintainable over time.

Regarding the structure you propose, what do you think if we adapt it this way? 🤔

.
├── apps
│   ├── console
│   ├── graphql
│   ├── grpc
│   └── rest
├── modules
│   ├── module1
│   │   ├── application
│   │   ├── domain
│   │   └── infrastructure
│   └── module2
│       ├── application
│       ├── domain
│       └── infrastructure
└── shared
    ├── application
    ├── domain
    └── infrastructure

This type of division, paying attention to the Screaming Architecture, could indicate the following:

  • This repository should only host one Bounded Context, since no high-level segregation of folders representing the different contexts is established.
  • The modules directory contains the different modules/features of the Bounded Context that is represented by this software artifact. I opted for modules since it is more linked to the concepts of Domain-Driven Design.
  • The shared folder represents the Shared Kernel. In this case, all the utilities of the different layers can become common between the different modules.
  • The apps folder reflects the presentation layer, in which the business logic and use cases of the application can be exposed using different technologies.

I would like to hear your opinion about this proposal. In this way we can reach a structure that is as appropriate as possible and that can fit a large majority of developers who have the same concerns as us.

Thanks again 😉

from express-typescript-skeleton.

robertgr991 avatar robertgr991 commented on May 20, 2024

Hello!

Thank you for the detailed response!

This step is an important one and it's better that you didn't proceed with it in a hurry.

I agree with your proposal, here are my thoughts:

  • Indeed, the proposed architecture suits the scenario where only one Bounded Context is hosted. Should we discuss about scenarios where teams would like to implement multiple Bounded Contexts as a Monolith, in the same repository?
  • modules aligns more with the concepts of DDD.
  • Correct, the shared utilities should reside in the Shared Kernel directory, to not pollute the high-level overview.
  • Where should each module implement it's Presentation Layer logic? Should it be implemented in it's respective directory and injected in apps/rest, for example?

As a side note, this changes will steer the template a little bit away from it's initial objective of proposing a template for a REST API.

from express-typescript-skeleton.

borjapazr avatar borjapazr commented on May 20, 2024

Hello again, @robertgr991 !

  • The intention of the template is not to create a monolith or a monorepo in which to contain several Bounded Contexts. I prefer simplicity and to keep contexts physically separated (in different repositories). So this template will only deal with the use cases of a specific Bounded Context. What do you think?

  • We confirm modules as a directory to contain the different features.

  • We confirm shared as the directory to contain the Shared Kernel.

  • Regarding the presentation layer, in my opinion, I would manage everything in the different subdirectories of the apps folder, one for each technology used (rest, graphql, grpc, etc.). In this case, a REST API is being implemented. I reason it in the following way:

    • I think that the applications that will represent the primary adapters should be managed independently. Why? In my opinion, use cases can be consumed in many different ways and using different types of technologies. For example, rest, grpc, graphql, console cli app, etc. If we add one more directory to each module to contain the presentation layer, we would be polluting and losing focus on the functionality itself. In the end, it's about the different clients that are going to consume the use cases of the different modules. Why does it make sense for secondary adapters to be in each module's infrastructure directory? In my opinion, despite being infrastructure, they are necessary for the application to work. For example, we need data to be persisted, emails to be sent, messages to be obtained from messaging brokers, etc.

    • On the other hand, imagine that we want to eliminate an app, for example, the REST API. If we delete the apps/rest folder we would have already eliminated that presentation system. If we had this same thing distributed across all the modules we would have to go module by module eliminating each folder related to the REST API.

    • Another point is that, if you want to externalize and take this layer to another repository, it could be done without too much effort. Imagine the case in which we encapsulate the core of our application, that is, the modules and shared directories, in a library. That library could be used by any app with any technology. In short, applications act as mere clients of our domain. Always through use cases.

    • The last point is that, if we try to apply the Screaming Architecture to this topic, it is easier to understand what the entry points of our application are if we have a directory that contains all of these implementations. If the presentation layer were distributed across every one of the modules, we would have to dig into the code to find out how we can consume our use cases.

    • This is an example that does something similar to what I propose.

    • I'm thinking that maybe it would be more convenient to rename the apps directory presentation, what do you think?

This is my humble opinion. As I said in the previous comment, I am open to any other proposal.

Thank you again.

from express-typescript-skeleton.

robertgr991 avatar robertgr991 commented on May 20, 2024

I think we should continue with the use case of implementing one Bounded Context as this is also easier to adopt. The means of communicating between Bounded Context can be different depending of the context and it's more difficult to create a boilerplate for it.

Your arguments for keeping the Presentation logic decoupled from each module are good and I agree, in this case, as presentation layer is the least coupled with the other layers and the flexibility of removing/moving a specific system with relative ease, speaks for itself. This will also set the rule that it's not the responsibility of the module to present itself, so to speak. Keeping the presentation layer inside each module may also contradict Screaming Architecture because technology-related terms would be used for those filenames.

As I recall, DDD doesn't have specific terminology for the Presentation Layer and different architectures use different terms, but, to be fair, I think presentation is more concise and more frequently used in literature. apps may also sound similar to application layer to someone new to the architecture.

I think, so far, this clarifies the direction in which the architecture should point to, unless others join the discussion.

PS: What do you think about Architectural Decision Records? Should a boilerplate have an opinion on this matter?

from express-typescript-skeleton.

borjapazr avatar borjapazr commented on May 20, 2024

Hello again, @robertgr991 .

Thank you very much for your reasoning and for giving your opinion regarding these issues. My opinion is the same as yours. As a summary, here are the basics we have established after adopting the use of Vertical Slice Architecture.

  • This template/repository is intended to contain and represent a single Bounded Context.

  • The modules directory will contain the different modules of the Bounded Context.

  • The shared directory will contain the Shared Kernel.

  • The presentation directory will contain the presentation logic for each specific technology: rest, graphql, grpc, etc. (You are right when you say that DDD does not specify a specific terminology for this layer. It depends on the architecture or terminology being used).

As a result, the target structure is as follows:

.
├── modules
│   ├── module1
│   │   ├── application
│   │   ├── domain
│   │   └── infrastructure
│   └── module2
│       ├── application
│       ├── domain
│       └── infrastructure
├── presentation
│   ├── console
│   ├── graphql
│   ├── grpc
│   └── rest
└── shared
    ├── application
    ├── domain
    └── infrastructure

On the other hand, linking to the question that you asked in your previous comment, I would like to comment on the following:

  • Regarding the Architectural Decision Records, I think it is a good idea to contemplate them in the scope of this template. Not to establish concrete and very forced decisions, but as a guide so that if a person uses this template he/she knows how to create these definitions and conventions through these records. That is to say, to create an Architectural Decision Records template using the decisions made for this project as an example.

  • Concerning the Use Cases or Application Services, do you consider that Ports/Interfaces should be defined for each one of them? In my opinion, use cases should only orchestrate the business logic and are not subject to varying implementations. I have read many kinds of opinions regarding this topic, but I would like to know yours as well.

  • Recently I was thinking about adding both Domain Events and Integration Events management to the template. Documenting myself in various ways, reviewing business projects in which I have participated, etc. there is a topic on which I do not find an absolute consensus. It is about the Integration Events. In which layer would you say it would be more convenient to handle them, in the application layer or in the presentation layer acting as another entry point to the application?

PS: Sorry for opening so many topics, but this way we can discuss interesting questions and give our opinion to improve this template.

Thank you very much for your time 🙏.

from express-typescript-skeleton.

robertgr991 avatar robertgr991 commented on May 20, 2024

Hello!

  • I agree to not enforce a concrete decision on the Architecture Decision Records, it should act as a suggestion for the teams that starts to use this architecture on how can they document future changes, but they are free to remove it from the start. As such, it should reside in a different directory. I recommend a Lightweight Template.1
  • The opinions are divided on this topic, but I recommend using an Interface as to not depend on an Implementation, even though, most of the time, there will be only one implementation. Maybe the team wants to rewrite the use case and run both in parallel to run a benchmark or observe other metrics. Of course, this can be done inside the use case implementation, but this will increase it's complexity. Using an Abstraction also works better with Dependency Injection.
  • Integration Events are published from the application layer and, in my view, should also be handled inside the application layer because it's a way to communicate between our internal applications. The presentation can also be consumed by various external clients or even customers. for example, Webhooks.

It's good to discuss these various topics as it sparks different ideas and also acts as a learning/recalling exercise.

Footnotes

  1. Lightweight ADR

from express-typescript-skeleton.

borjapazr avatar borjapazr commented on May 20, 2024

Hello again!

  • Perfect, I think it is a good approach to use Lightweight ADR. In this repository there is a real example.

  • Ok, I agree with what you say. I think it is a good idea to include Interfaces for the Use Cases and thus not depend on a specific implementation. Even if the implementations are not going to vary much since the business logic orchestration is not something very variable, the Interfaces can be useful for other topics like, for example, integration, performance or load testing.

  • I agree that Integration Events should be published and consumed at the application layer. In the end, this type of event belong to the core of the domain and business logic, so it would not make sense to attach it to the infrastructure layer to finally execute an action in the domain. If I asked you the question is because in many projects I have seen developers add these consumers or event handlers in the presentation layer (for example, in the presentation/kafka folder). Through those handlers, they call the use cases and execute a specific action. In a way, it could fit me, since in the end it is an input/primary adapter that acts as a dispatcher for the business logic. But if we look at those integration events from the point of view that they belong to the execution of a distributed business logic it doesn't make sense to capture them in the presentation layer.

  • About the last thing you mention in this point: "The presentation can also be consumed by various external clients or even customers. for example, Webhooks." Could you explain it more to understand it better? Thanks 😉!

PS: As new topics arise, for example, applying Vertical Slice Architecture, creating Use Case Interfaces, creating Architectural Decision Records, etc. I will create different issues to implement these functionalities or improvements.

Thanks again!

from express-typescript-skeleton.

robertgr991 avatar robertgr991 commented on May 20, 2024

I should have worded it better. I was trying to say that in presentation layer it is expected to reside the logic of communication with the "outside world" while Integration Events is an internal communication. But I see your point. I've seen this implemented in different ways, in presentation layer, in infrastructure layer of the Shared Kernel. I think this is more of an infrastructure problem than a presentation one, but I don't know if this should reside in the shared layer. What do you think about this?

In the end, this is more of a personal preference, as both presentation and shared/infrastructure will only act as a glue for the actual Use Cases.

from express-typescript-skeleton.

borjapazr avatar borjapazr commented on May 20, 2024

Hello again,

I've been giving this matter some thought. My opinion regarding where the Integration Events should be captured is as follows:

  • An Integration Event is an important part of a whole. Specifically, it is the mechanism that allows different Bounded Contexts to communicate with each other to finalize a larger business logic that is often distributed. That said, the management of Integration Events is done at the application layer. By management, I mean deciding what action to execute in the consuming system due to the event. The concrete implementation of the consumption of those events, using one technology or another (Kafka, RabbitMQ, etc.), should be the responsibility of the infrastructure layer. Thanks to Dependency Inversion, the event handler would be modelled in the application layer using an interface but implemented in the infrastructure layer. Here are a couple of examples:

  • Regarding what we were saying that in some cases there were developers who handled those events in the presentation or infrastructure layer, I think that would not be entirely correct. If it were done in those layers, the objective of having the architecture or the folder structure itself self-explaining and reflecting the intention of the system would be lost. Also, the infrastructure and presentation layers are the most volatile. That is, they are subject to constant change, whereas the application and domain layers should be stable over time.

  • Another topic is that we may want to add event-based mechanisms as a presentation layer to execute complete use cases. In this case, after capturing an event in the presentation layer, the complete necessary business logic would be executed. But we would not be executing business logic that belongs to a use case that was initiated in another Bounded Context and is part of a whole. In short, it would be another way of exposing the application to the world. For example, we would have a REST API that by making a POST call /api/v1/users would create a user and, on the other hand, we could have a Kafka topic, to which the presentation layer would be subscribed and receive events that reflect commands to create users. Maybe the difference is hard to see with respect to an Integration Event, but for me they are different scenarios.

Now, another topic occurs to me that is quite curious. And it is how you would implement Webhooks in an application that follows this type of architecture we are dealing with. For you, are Webhooks part of the presentation layer or should it be treated as something in the domain? That is, if you had to call an endpoint registered by a subscriber (webhook url/callback) every time a certain event is triggered in the system, how would you model it? I can think of two main options:

  • Create a new presentation sublayer in presentation/webhook and there subscribe to the events and make the POST calls to the different webhook callbacks.
  • Model the Webhooks as another module/Bounded Context of the system and treat it as something related to the business logic of the application. So, I would subscribe to the events in the application layer and based on them I would make the call to the different webhook callbacks through specific use cases for it.

I would like to know your opinion on this 😊

from express-typescript-skeleton.

Related Issues (6)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.