This is one of my favorite videos of yours so far! The patterns demonstrated in the Clean Architecture sample that you showed are all too familiar, and I've tried in the past to get people to see why this is an issue. There are so many things to change when any use case changes, and it's super easy to miss something. It's unfortunate that so many people still cling to this file structure.
Thanks. This was talk I did recently at a conference so I decided to just record it. Really it's a collection of videos I've done in the past just mashed up together.
I feel validated by this video haha. I've had this discussion and demonstrated the separation of concerns alongside cohesion to many colleagues and I'm astounded how many are perplexed by the idea because "it doesn't follow the known patterns." Thanks for sharing this video!
In the past 3 weeks we’ve merged 4 micro services using clean architecture back into one of our main projects. We’ve started removing the clean architecture layers from those projects that have been merged in where it makes sense to. We are amazed at the results! We have gotten rid of so much superfluous code! We generally have 5x less code/files. The code is so much simpler (and cleaner, funny enough). It’s easier to find the places where to refactor… the clean architecture was hiding many places where we could improve our code. And the benefits of removing unneeded micro services has also removed superfluous connecting code. It’s been great! What seemed like an unmanageable nightmare now seems like it can be tamed and is now enjoyable to work with again.
@@cicerofoscarini8890 we’re a small team of 4 devs and a couple of external contributors. Usually there’s either one or two devs working on each project. Should never have gone down the micro services route as our team was too small to support it effectively.
And that is exactly why this makes sense. Too many people are implementing without actually understanding why they are doing it. KISS is appropriate everywhere, or to use Einstein's methodology, "Everything should be made as simple as possible, but not simpler.". I think he captured it well.
Sounds like a decent decision. However please remember to update again here in 12 months id be curious to see how it pans out in the long term. There arent often golden bullets just different sets of problems and tradeoffs so will be interesting if you hit different issues.
Why not organize first into features, then layers? The problem with not making layers visible in the project structure is, that in a team with different skill levels present I'd say it is almost impossible to keep any unobvious layer concept intact over time. So you WILL end up with domain logic coupled to infrastructure and application concerns.
There are always tradeoffs. The trouble with monolithic code patterns is that you often find similar usages across the vertical layers that require you either to couple one tower to another or else result in code being replicated across towers. I have seen the results of this pattern of not having a single idea of a product across business domains, and you end up with massive code replication and systems breaking routinely in production because one team subtly changed its definitions and business logic and this impacted other systems but was not caught in testing. The number of test cases that have to be written vastly increases and so forth. In a real sense people are just pendulum swinging back and forth between different design patterns and as they get more familiar with one they start getting grass is greener on the other side issues, and start longing for patterns that were previously abandoned.
Whatever structure you go with, you still have to have a single representation of a determined concept in your code. In vertical slicing, what stops you to have kind of a "core" package that shared smilar code ?
You don't have to replicate the code; just extract it into another module and use that module everywhere it's used. And it should be sufficiently uncoupled with the modules using it to avoid future refactoring, just as its identical implementation in a layer would be. The only difference is now you're calling the layer in each module that needs it from within that module, rather than on all modules unconditionally.
Awesome content! This is the best video about layer architecture I have seen lately. I believe people create templates with this module structure to enforce the prevention of compiling a module with a wrongly directed import statement.
Excellent explanation of something which should be interviewed for during every IT job interview, in my opinion, to filter out the pattern-as-dogma-crowd.
We use this organization structure at work. One thing that can really help, especially with juniors, is something like ArchUnit (this is for Java). It helps to validate architecture, such as guaranteeing certain packages don't reference other packages, or even that certain named classes don't reference certain packages. We have standard naming for standard purpose classes (e.g. *Repository, *Service, *Request, *Controller, etc), so we also make sure a class doesn't contain that standard name in the wrong package, or a standard name doesn't reference the wrong layer. E.g. *Service shouldn't access *Controller. This can be especially beneficial if you don't further divide things up in a vertical slice.
That sounds very useful, I was worried about how to enforce the architecture and had never heard of packages that serve that purpose. Thanks for sharing!
The analogy to a recipe is perfect. Not everytime we need to do surgeries. We can do them sometimes in different zones. Not in the whole body. Thanks for the amazing info and knowledge share. World gets better when we know what to do for us and clients.
Great video, awesome points and I mostly agree with all Clean Arch presented concepts! I do have a point to add though, the dependency direction/coupling of the project exemplified at 5:44 seems wrong (or confusing) to me according to the Clean Architecture book's concepts (unless the Infrastructure layer shown is an Adapter layer called Infrastructure and not the real Infra layer which would be coupled with a specific tech choice). That is, in my understanding, WebUI (blue layer/external layer/frameworks) must not reference an "infrastructure" layer (which should be blue, not green, cause it's also an external layer... the green layer is an adapter to the infrastructure where you must invert the dependency direction (DI)). A "purist" (by concept) clean architecture in this example (IMHO) would go: WebUI ->| Application -> | Domain _________| Application (I)|
It’s not actually a full vertical slice. In your example you are only slicing up the application layer but still leave other layers (i.e. infrastructure). Most examples I have seen also do this, but they don’t achieve what the commonly used image actually displays (slicing UI, application, domain and infrastructure). Thus, you still end up with layered architecture in the end. However, if you were to be consistent with the slice, that would mean plenty of code duplication, for instance, repositories would be sliced across features, and so forth, and that is not ideal.
"repositories would be sliced across features, and so forth, and that is not ideal" Sometimes it's ideal, sometimes it's not. Sometimes features use different repository methods making it pointless to share. Sometimes repository methods try to do too much to serve the features that share it making it bloated and inefficient. One feature might need 2 pieces of order related data from one data source, another might need 50 across multiple data sources. I always start by putting my repositories in the slices and then as the feature set grows I pull out what makes sense to share and leave the rest. If the shared resource gets bloated later on, I break it up to the slice level.
@@bike4aday I feel like there is a lot of exploratory work involved in Vertical slice architecture and we are still "getting to know it". There are also mixed opinions and criteria of what should or shouldn't be the case with these. For example, if we dogmatically did it, with maximum feature isolation which does not leak out to other features, like in article "Out With the Onion, in with Vertical Slices", we also end up with *funny* things like css and FE code along with all other BE code.
You aren't going to have a DbContext per request. Nor would you have an aggregate per.. It would be for a feature set. The point isn't share nothing. It's putting related things as close together as possible.
I find the distinction easy to make. Business processes/operations are self contained - they are the slice. Loading data for a slice is part of that business process. 2 processes loading “the same” data is just coincidence. I won’t go too much into the pointlessness of using repositories with EF core ;) “Infrastructure” is shared code. It has nothing to do with the business problem you are trying to solve. It is utility/convenience. e.g a “GitHubClient” which essentially wraps an “HttpClient” and exposes some specific methods to interact with GitHub/some 3rd party API
@@kabal911 It actually typically has a lot to do with the problem you are trying to solve, for example, Order entity is an infrastructure piece (DB/ORM). Same goes for Order router. Probably rather than using infrastructure which confuses things it’s better to use “shared”
Just realized that GraphQL is CQRS by design (query and mutation). And using Apollo Federation and extending shared types is a way of slicing a common schema model and allowing each business area to add their specific view about e.g. a "Product", which is a cohesive architecture pattern by design. The only way to mess up coupling at that point is shared libraries which may still add coupling between different business areas.
First off, thank you for rounding it all up in one video, I will spread it as far as I can, with this channel you're doing the code better! Could you make an addition to this video with practical examples? I still have so many questions on what do you do when you need to communicate between features, or creating a feature that depends on multiple feature, or how do you share the same model across multiple features? Thanks again!
Great material. 😃 Looking back at the projects I've been involved in. I am also having similar thoughts. I generally agree with this approach. However, it is important to remember that the entire team should have a similar level of knowledge/understanding of the subject. Especially people who are new to join such a project.
😮 where has this video been all my life? I've just spun up another service using clean architecture in the classic form....thank goodness I've caught this early!
What about same application code being used by API, or gRPC or WPF forms should we put all of those “presentation” classes together? I mostly agree except with having the controller class bundled together.
This would exactly be the reason to have those in separate projects. Projects/assemblies are exactly for this purpose, reuse. So ya, don't put frontend with the rest of it.
nice new perspective in structuring... the cake slices example and they way explained cohesion with it... so beautiful. it really makes sense. wonderful
when using a single database, is it worth splitting the domain into smaller ones for each function, or is it worth using one super-domain project with a description of all entities? for example, if there are about two hundred related tables, when updating one of them, there will be a lot of work when updating its entity locally for each function. What is the best thing to do?
Separating by project/dll/package, is more about independant delivery/deployment and also team dependency, so it's better to separate by project/dll/package, trying to reason in terms of Dependency Inversion Principle (from SOLID) as suggested by Clean or Hexagonal Architecture. It's about seeing deliveries as PLUGINs for an independant package.
I think that a good way to think about coupling and cohesion may be the opposite as everyone does. A single unit may have low cohesion (lot of responsibilities) but it has 0 coupling by definition because it cannot be coupled to itself. When you split that thing in two to make it more cohesive, now you got coupling. That means that splitting things increases coupling and cohesion so they are opposite forces. It's an art to win in this situation and applying prescribed solutions doesn't help. Also I see a lot of people that think that the mere act of splitting things reduces coupling when it's actually the opposite (e.g.: Microservices)
1) This argument is somewhat similar to Uncle Bob's one, "screaming architecture", discussed in its book "Clean Architecture" 2) Using different projects in .NET enforces the acyclic dependencies principle, i.e., you cannot create circular dependencies. Without this division, you have to use tools like NetArchTest to have the same feature 3) For Web APIs, it is possible to use the REPR pattern (e.g. in .NET, Ardalis ApiEndpoints or FastEndpoints NuGet packages) to favor a cohesive structure of the files 4) The example of a product, discussed in the Informational and functional cohesion section, has been covered in detail in Mauro Servienti's talk "All our aggregates are wrong" Thank you very much for your clear and thorough explanation! I prefer this longer format. p.s. I'd really like to see a video with a list of recommended books!
*2) Using different projects in .NET enforces the acyclic dependencies principle, i.e., you cannot create circular dependencies. Without this division, you have to use tools like NetArchTest to have the same feature* I've inherited a few projects over the years where this is most definitely not the case.
The only thing that overthinking architecture ever did for anyone is get solutions stuck in a dependency catch-22 What software architecture has taught me over the past 20 years is that software architects are addicted to solving non-problems. In compiled DLL languages these problems that we are trying to solve are basically build into the DLL model itself. Basically projects: the architect’s favorite folder. Completely made up doctrines like no upstream dependencies are primarily due to how compiled languages have separate DLLs requiring each over instead of just compiling the gdámn executables from the various dependencies. The no-circular referencing of BLs is an OOP problem, to which the solution was always staring us in the face: less architecture, not more. Functions, not BLs. The BL itself is a failure to do the basics right in the first place. KISS and separation of concerns. The BL is itself an encapsulation of concerns, but a function isn’t. A function can have a dependency on another function that has a dependency right back on itself, and it’s fine. We even gave it a fancy word. These same original DLL and BL problems just keep getting more and more fancy ways of solving the same self inflicted problems of too much architecture and not getting the basics right. What do we do when one rigid BL needs to call another rigid BL that itself depends on that other one? We pull that functionality out unto a less rigid “helper”. And those helpers are just functions. See what we did there? Now just take that same idea to the limit and get rid of the rest of the artificial, self-inflicted rigidity, and all of these fárt-sniffing solutions to the same old problems we created for ourselves go away, by OBEYing the fundamentals of software architecture. 1) KISS 2) SoC Do these two things and vertical slices becomes an eldritch solution to a problem that no longer exists. Then compile your in-house code to a (A) dll, and your upstream/downstream solutions become a “wtf are you talking about? What is upstream mean?” It means that architects treat projects like folders, but to be fair, these problems are baked into the project based OOP programming models themselves.
Such a convoluted solution for a non-problem. The motivation for vertical slices is always having to dig through folders to get all the files for an entity/feature. Whenever I find myself doing this in layered architectures, I just use my editor to search for the filenames that contain the entity name and then open those. Problem solved.
Not really. What is the size of your codebase? How many different domains are mixed there? Do you need to migrate some of them at a time, or everything applies to all of them at once?
With a legacy solution, a layered monolith, how would the vertical slices be introduced into the same monolithic solution without fitting it into existing (non-clean) layers? Maybe a new project? Then vertical slices within that same project? Or a separate project or set of projects from each slice?
For anyone in the Node world and don't know NestJS, it has a wonderful ability to slice the onion so that you can build modules of feature functionality, which are like mini-onions. Very powerful!!!
The whole onion architecture is basically intended in the Node backend world with the request context being passed through a series of middleware functions. It's trivial to keep the style going from the middleware to the actual endpoint functions if you use typescript decorators, since they're just functions that wrap the function they annotate and are executed in the same order they're written. So you can have @Authorize @Validate @Whatever endpointFunction() { ... }, and it'll run authorization, then validation, then whatever and then the function.
Great video, great presentation. Something was screaming "bounded context" at the back of my head all the time, though. Your "features" are a level of granularity lower, but the example of "product" meaning different things to different users is how I understand "bounded context". Two use cases may be talking about the same physical entity but each views and handles it in their context, using their vocabulary and their features.
Not wanting to take away from what has been shared because it's awesome, as ever. As the video identifies, there is some value in cohesion along horizontals too. The domain is an obvious one, but the API can benefit too: HATEOAS is a love letter to cohesion across the top of the stack.
Excellent explanation of the concepts. Many of your points about the creation of turds I have seen created over time. The importance of the different contexts against the same data is critically important. Excellent video!
Thanks, Derek, very interesting video. I have a question, though. What happens when you have multiple clients that have the same feature but that show different data? For example, you could have a SPA, an MVC app and a mobile app that all need to display different item data. How do you do this? Typically I see people returning the full data with all the fields for each one, but if you only wanted to return the necessary data for each how would you achieve that? Would you add query parameters to the endpoint that specify which fields you need? But what happens if one of these clients need the items to be joined with another table/entity for additional data? I haven't yet found a good solution for this.
You're getting into BFF (Backend for Frontend) concepts with this and view/ui composition. I haven't done one exactly on this, but have talked a bit about it in here: th-cam.com/video/ILbjKR1FXoc/w-d-xo.html
I don’t see how this is really different to “traditional” architecture. You would still need separate/different endpoints if your REALLY wanted to support different data shapes. The problem is not the code organization, but the endpoints/REST. This is ultimately what something like graphql tries to solve.
I think this might be your best video yet. It provides a lot of context to how you think about architecture that isn't always clear in the shorter videos. I think a lot of those will make even more sense if I re-watch them after having watched this one. This is way more than just a video about project re-organization. It's about a better way of tackling problems (features) such that you use the best code for the job. Don't create a domain layer for a simple feature that only needs crud. Doing so will be the equivalent of wearing snowshoes in summer. You can still get around, but it's going to be longer and more complicated than without them. Likewise, using CRUD for a feature with complex business rules is like walking through deep snow in just your sneakers. You're going to fall down a lot. Anyway, great job. Super helpful!
Ya, this video is really a combination of a bunch of videos that I've already done. Just makes the whole concept in a more cohesive (pun-intended) video.
22:20 i would prefer not to put everything in one file and rather make separate files in the same folder why? because i dont like scrolling, and when i open a file i wanna see the thing it does. i also place shorter functions/methods higher than the longer ones so i can see many thing without scrolling so much. and time to time i open stuff next to next on the IDE and its not possible or hard if they are in the same file. i also dont like nesting stuff into multiple folders deep, but i would just put everything into their own file in the same folder
I love the way you say "I'm not that way" .. its kind of implying that people with "those" beliefs are somehow misguided, but you're not making a value judgement out of politeness/repect
So kinda summarizing this I came to some conclusions. 1. You don't need to split small things into different files. Interfaces, validators etc. that belong together, should live together. Unless those things are shared across multiple components. 2. You need an API to access a component/module and that's the only entrypoint to it. Whatever happens inside that component is it's own business and it doesn't expose internal behavior. 3. Design your application by doing domain partitioning, not technical partitioning.
So vertical slices is about expressing the "virtual" vertical slices across a layered architecture. My problem is that even you go great lengths to create vertical slice at some point the slices will share functionality with other slices. Excellent example is authentication and authorization, the functionality is the same for all slices. If you plug it into all or most slices it becomes a layer. At the end you do not want to directly depend on the data persistence solution, so you will build an interface and implement a service. Slices without interacting with the domain are the definition of services?
One of the biggest advantages of layer architecture is that you can control what you expose to the next layer and also prevent spaghetti code. With just vertical slice, it's really hard to enforce decoupling and when working with a large team of developers people are going to mess things up. One idea I have in mind is having a Clean/Layer architecture and each layer is a separate vertical, trying to merge the benefits of both ideas
I had the same experience. Working with folder structures and in a pragmatic way was extremely difficult with large teams or teams with not enought seniority. In the end you need silver bullets for everything. Many projects to avoid coupling at compile time , mediatR to avoid fat controllers, even microservices just for damage control the devs etc etc. Having said that... great video @CodeOpinion , love your work.
Grouping classes (that belong to one layer) in a single package, module or project has always been counter-intuitive for me. Just because a class has the "Controller" or the "Activity" in its name (as a suffix usually) doesn't mean you have to put all the controllers in the same holder (i.e. folder), as these classes have very little or nothing in common. Instead, breaking down the application into the features (rather than layers) and then designing every feature as a single component/service, which contains many cohesive sub-components/classes, is a much more flexible architecture, if you later decide to, let's say, extract one feature as a micro-service or simply replace the libraries for event handling, messaging or even introduce a new DB. I am very happy this video recommends/approves this very same approach,. One more thing, working on my bachelor thesis (ages ago...), named "The Cohesion in OOP", helped me to question the "layered" approach from the very beginning even though I had a very limited experience in software/code design, so I think it is very important to cover (or at least get familiar with) the theoretical aspects of the software engineering. It certainly helps (from my experience) to recognize and avoid the scenarios that could give you troubles in the future.
Great video! How do you feel about VSA on the frontend? I'm currently researching how to scale large frontend applications using design patterns, but a lot of resources only talk about the backend.
This is all fine and dandy, and you make some good points, points I wish more 'legacy' companies would consider when hiring people who actually WANT to make the systems architecture cleaner. Unfortunately, the reality is many legacy companies (perhaps not the kind your example shows) operate on a "let's put all our business logic in the database, but never document it or track changes" mentality. This completely nullifies and obliterates any sensible architectural concerns (not saying MVC is good architecture, quite the opposite, but at least it can be refactored and tracked by subversion). TLDR: Document your SQL in some way, or resign as a developer. I'm tired of having to guess what your complicated storedproc does because you spew a bunch of CAST()s and SUBSTING() without actually naming your variables (especially table aliases) something sensible and self-documenting.
There's also the "SQL is the way, the truth and the life" mentality, which is retarded.... there ARE other dbs out there and not all of them are trendy toys like MongoDB (Cassandra, Redis, and Neo4j are all exceptional at their jobs, yet I keep landing contracts with everything being stuffed into SQL Server like it's the 1970's)
If the UI is within the same platform, then next to it. For exmaple, if you're talking about MVC, all of them can be organized in code together. If you had a iOS app and a .NET Backend, that code won't live together from a development view, but it it is from a logical view.
Your controllers should handle only the HTTP request-response cycle. Such as receiving inputs, calling services and return responses like succes or error messages. Your logic should be handled through a Service layer which get called in your controller. This is done for many reasons: - Reusable: for example a UserService.EditUser(..) function can be called inside the ProfileController for users and inside AdminController for admins. - Maintainability: If changes happen you only change the Service function and both controller endpoints will work right out of the gate. - Testability: simulating HTTP requests to hit your controller endpoints during testing is annoying and difficult. It is much better to instantiate the service class, execute the function and then test it. - Scalability: as your app grows you won’t lead to code inertia where it’s difficult to change or add new features. This separation will keep you to ship rapidly. MediatR is excellent for keeping controller functions at max 3 - 4 lines of code and transfer all your business logic to separate Service layers
I’ve followed vertical slice architecture on a very large project (with the nested class for naming) - one thing we don’t do is the controller per feature which is an interesting idea. We have a partial controller for each entity, with a another partial for the actual feature route. I think I like your way. Not sure what impact it would be moving from 10 controllers with around 500 actions, to 500 controllers?
thank you for this explanation, but I had some thoughts in my mind regarding this as I know clean architecture is not just meant for cohesion but also meant for isolation between business logic and infrastructure, in clean architecture you need to build some sort of abstraction layer using facades and adapters to avoid technology changes, so for example if you wanted to change the database, you can build the actual implementation that matches the adapter layer and here you go you changed your infrastructure without affecting other important areas, but in vertical slice architecture, while you are grouping related things together, if you wanted to change your db to another one, how much change should be done?
Wonderful video! I love it. I have a question. If I share a model between features, I share property and behaviour that don't interest all the feature. Is there a way to solve it? Example a shared basic model only with SKU property and treats with property and method, in this way I can share logic, I can build custom model for each feature. The same for repo. I don't delete method available on update feature. (I understand tha I can execute a simple query instead a orm function.) Is this a good way in your opinion?
You don't have to have a "model" that's shared at all if you want to simply use a transaction script. I only suggest it if you don't have consistency concerns and it's really just a data model. At that point your transaction scripts are implementing the behavior. If you do have a lot of logic, complexity, and need consistency, then stick to an aggregate. Check out: th-cam.com/video/aLFMJ_frafg/w-d-xo.html
How do you do domain validation in a reusable way within this architecture? This would be outside of your normal validation making sure this is a valid email. Something along the lines of ensuring the email is unique or more complex scenarios where you have to do a DB call to validate this is a valid action given the persisted settings.
Interesting one. Do you think, we should have seperate projects for logical concerns, rather than projects split into clean structure like adding domain, application etc? Could these be folders in a single project, for vertical slice?
You can, but you don't have to. Projects are often just use as a physical barriers or to prevent circular referencing. You don't need multiple projects at all. You can just use a folder structure within a single project. All depends on what your needs are.
I wrote a Blazor app for work a few years ago and followed the Clean Architecture structure. I've been on the hunt for a good way of structuring apps for years, and I tried it out for this app. As I was developing it, it kinda made sense. Then I deployed the app and went away for a while, doing other work. Then I had some change requests and bug fixes for the app. And looking at it now, it is a complete mess! Exactly as you describe here - files in so many different places across several projects, so making a simple change can involve changing ten different files. At some point, I'm definitely going to rebuild the app and base it on a much simpler structure. VSA might well work for that. It'll certainly be a lot better than what I have now!
Yes yes and yes. Started my first job as a junior feeling ready to make some nice layered code with nice abstractions and it looked awesome when i was done with the initial codebase, only to absolutely hate whenever people asked for new features / changes, because i had lost the overview. I was afraid of changing stuff as the use cases changed Lately i've been grouping stuff based on ownership even if that called for some duplicate code here and there, but it guaranteed me i knew what would be affected whenever i went in to make changes.
@@CodeOpinion was great! Though it raises a few questions. If you do sliced architecture and need some background jobs like azure functions. How we do it is sharing libraries between the api and the function app to make sure both are deployed in case of underlaying changes. In those cases we actually end up with some middleground where we make a Core library that has shared data models and infrastructure to avoid having to duplicate models and forget to make changes. I'm also interested to see how a VSA would look without MediatR. I guess you would end up building something similar? When you put controllers in same file sliced out, how do you deal with shared routing eg. api/products... do you put that as static constants? Have you tried using minimal apis with this approach or the nuget FastEndpoints? I'm also all for putting multiple classes in the same file. This is common when doing frontend do why cant we do it in backend? :) i have managed to change the mind of one in my team, so now we do it hehe
@CodeOpinion may I suggest this is very much the idea (loosely) of how Unix handles things via scripting together a bunch of relatively simple utilities: not all of them have I/O other than standard I/O and stderr, nor do they need to: they may instead set the system to a particular state instead as they have data filtered through them, or any other arbitrary number of things. It’s true, it’s not a perfect analogy. When building up complex systems of scripted components like this in processing pipelines, it’s easiest to do it incrementally along the way. Ultimately an “application” is just a bunch of simpler processes chained via the script. I’ve for many years considered this is likely a better way to do things within a single process instead of the Unix method (or using PowerShell, which is more OO) of composition by smaller programs. I’d suggest a great advantage of doing things with these smaller vertical slices however it is done, is it isn’t as painful to obliterate something you wish to remove or replace, in all aspects: all code is easier to delete, and code that is easier to delete is easier to perfect, where the ultimate perfection is no code at all: that’s one bit of code you can be sure won’t be buggy 😉
Amazing video and concise examples, nice work! This is a common topic and all because IDEs don't allow the creation of tags and scopes for project structure visualization. I'm currently working on an extension that allows you to create your own project layout views using annotations. Then arrange your code and files as u want ;)😉
Great video with a lot of detail. But, one file having the whole slice/feature in with potentially 100s or 1000s of line long sound terrible, most likely would have regions all over it. One reason why layered/clean architecture is so popular is everyone knows it and understands it, when working with external developers this is beneficial as its something everyone can relate to.
Introducing and selling vertical slicing is not too hard in my experience. Developers get it instantly, including external developers and fresh from college juniors. The benefit of not having any significant merge conflicts when working in parallel (feature/developer) is a big selling point. Other architects are harder to convince, like always.
Multiple classes in the same file is code smell to me, even if IDE's allow you to find it. It makes it harder to modify than necessary when you don't have it collapsed when scrolling around... Especially if they have similar looking code. There's a reason why classes are per file. I agree with DDD though and organizing by logical/feature-based layers. Microservice architectures basically lend themselves to this design strategy.
At 8:11 would be really helpful for random access viewers to label bottom as “business domain” … one of these is not like the other. (If that’s the case, not an msft person)
Very nice video with a clean explanation, I like it. However, one thing still bothers me; why did you put the controller into the vertical slice? Shouldn't it be at the edge, like persistence? Or another way around, shouldn't you also put the persistence into the same vertical slice?
Persistence was shared several requests, hence why it was directly in the same file. The controller with the single route was in the same file because it's specific for that request.
Great video, it's a great structure for larger projects. I'm having one issue though, and I can't find where in my configuration I'm doing this wrong - When Nesting the items under a static class (22min in), it all works EXCEPT for the controller. As soon as I put the controller inside the static class, it fails to find and expose it. Any tips on making this work?
At first specifications looks nice but then you realize they are a very limited overhead over a dbContext that is already an abstraction of the database. I loved how you grouped your features and files with multiple classes per file.
Can you make a video? Over migration from relational to no sql. Taking reference of schema you shown at 23:00. What need to taken care. When need to embed, when we should go for relationship etc. I would love hear those key points.
Good points. But if these layers aren't physical separated, you will have people start directly referencing infrastructure code in your UI code. Then we're back to the 90's. There's nothing wrong with that, if your app is small. That ☯️ thing explains it all. It's a balance and it depends.
Excellent video Derek! I still have lots of question regarding Vertical Slices, but I'm getting to it... What's your opinion regarding that issue: Let's say I have a use case that involves 4 changes to the database on 4 different Services, one depending on the other to get the ID for example. These changes are within methods and they call other repository methods. How can I mitigate issues if something fails between one update and another? Rollback is not an option since I am working with different Contexts.
This also doesnt work if you work on a large application that needs to be distributed as nuget packages. Think Hangfire. It has support for different backends and infrastructures. Imagine if it distributed one assembly that had Postgres, Sql, Sqlite, Redis, RabbitMq etc etc. The huge dependency surface of adopting that into your project. Yet the cohesiveness argument says it would make sense to have say the abstraction for starting a hangfire job, and all of the implementations of that close together in the same source file according to this approach. Its not even an option its a non starter there. You cant even put those implementations into the same projects they have to be factored into projects per system you are supporting so you pass on the minimal dependencies to consumers in the name of stability.
I think I know where you going with this but view it a bit differently. Hangfire is a library that contains abstractions for persistence, vs what I'm talking about is application code that isn't cross cutting at all. Its specific to a feature, there isn't any abstraction. If you do have cross cutting concerns or shared types, (eg: events) those absolutely would be in a different project that could be shared via a nuget package. The point isn't to 100% have everything sitting right next to each other, it's to have code organized what can be together. What changes together lives together.
@@CodeOpinion yeah I agree there are two different paradigms here though. For the application case it makes sense because you aren't passing on implicit dependencies with the way you structure the code - no one else is "pulling" in your application to use in their scenario. This is unlike all the second paradigm which is more for things packaged up and distributed specifically as dependencies.. Maybe this is obvious; but just interesting for me to call out as someone who works in both spaces it would hint that there is no "one best way" of structuring the code. Then again if the project and build system was more capable perhaps indeed both of these paradigms could use the same approach and something in the project / build system would do the appropriate splitting to seperate assemblies / packages etc.
One of the best, I would not say video but guide, arround Internet about SW Architecture. You touched some wounds over here (like, c'mon.... have you ever read the name of the channel?).
Yes!!! nothing wrong with multiple classes in a file, had discussions on it... i like my interface right above my class in the same file. If i have 2 implementations of an IStorageService, one for local file storage and the other for writing an reading from a blobstorage, i like them in the same file. So glad i am not alone on this.
My thoughts: Clean Architecture, as prescribed many for .NET, is hell in a microservice system. Too complex. I can tell you that. But I get why people like it: For the project boundaries and the direction dependencies. Just don't use it for microservices! About the "microservices thing", a lot of developers are going to microservices because "that is what they should do", putting parts in separate projects/apps without the actual need or requirement to do so. "Microservice per table" is a great example when you shouldn't build microservices. Perhaps you should wait and get some experience with them outside a professional project, at least. As you show, there is more to it than just divide tables across projects.
Always great. But I have a non related question, my work experience is 10+ years. Now I want to become an architect. What will be starting point and path for me? Please suggest. Thank you!
I would like to know your opinion on how you would deal with the following scenario. Let's say we have an API project where we implemented features using Vertical Slice Architecture. All these features(slices) are implemented in the Features folder using the illustrated pattern from the video. Now we decided to have Blazor Webassembly as our frontend and we would like to reuse requests and responses(commands/queries) from the API project. What would be the most appropriate way to share these requests and responses? Would it be okay to create a DLL library(contracts) that would mirror our Features folder from the API project and declare there only requests and responses(commands/queries) and then we would reference this DLL from both API and Blazor projects? With this approach, it would be theoretically enough then to implement those queries/commands in the API project and we should be able to reuse the same code between the frontend and backend. Or is there any better approach for this kind of code sharing? Thank you
Thank's for this Derek. Is there a template such as the one for Clean Architecture for this approach? It's always much easier to start doing stuff in a different way when there's an example how this is done. If the template gains popularity people will most probably be willing to contribute to keep it up to date.
Loved this video, I think this is a really underrated topic. I'm not sure I agree with the framing that increasing cohesion increases coupling and vice versa. Take the reddit guy's data model as an example. If you were to stick whatever that app is into a typical layered structure but without some kind of vertical/feature slicing, you would end up with a ton of coupling caused by the center of the layers having very little separation of concerns. All the features would be extremely brittle to change in one another. This feels like coupling to me. I think maybe a more accurate framing is that high cohesion creates greater local coupling in exchange for the benefit of lower project-wide coupling. The intra-feature bits that do cohere with one another will be slightly more coupled - which costs much less because of their natural relatedness - but the rest of the project becomes significantly less coupled to each individual feature, which is a far more expensive kind of coupling. Curious what you think about that.
With this line of reasoning, all the tests should be in the same files also or if not the same files then in a file next to it. This might mean taking a dependency on xunit which accidentally gets deployed with your applications - this sort of thing can easily cause unexpected issues - when you see xunit assembly appearing in the browser of a blazor wasm app :-) I actually am really attracted to this, I just dont think our current project systems are set up for this. You'd want to set safety rules to enforce the layers and accidental misusage between classes. You'd want to exclude all test source files from being compiled into the binary when released for deployment but typically we do want to build the binaries in release mode once and test the same binaries that get deployed through to production so I see more problems there. Projects for layers arent as nice for cohesiveness but they are a compromise offering a level of ingrained safety through rigidity.
I absolutely wish and have said many times, I wish tests could live right along side the code your testing. If there was a meaningful way in .net to do this because of packages etc and not have them shipped/built in output would be great.
Hey Derek Thank you for the great content, i was messing around vertical slice arch for a while now and i really enjoying it. But there a question, if the controller and command and command handler are basically in the same file why do we need mediator then?
Something like MediatR separates ASP.NET Core from your application code. You're converting an HTTP request into an application request. You don't need MediatR to do this, but it forces the concept. However, if you were to put HttpContext or anything ASP.NET Core into your requests, you're losing some of the purpose. I find the true benefit if you're creating a request pipeline for your application (mediatr) request. There are other libraries that do this beyond mediator that also work out of process (wolverine, brighter, etc).
Vertical slice is not an architecture. Clean architecture is not the a problem but people understand that clean, onion or whatever architecture are application architecture like MVC is the problem. Clean architecture is a bound-context architecture so I think vertical slice is just a renaming of bounded-context, bounded-context is even better since it gives criteria (DDDesign way using ubiquitous language) about what to put inside the boundaries. FYI in the clean architecture book there's a chapter with title "The missing chapter" written by Simon Brown where we can find how to package modules in different styles and end up with package by components which is another name of vertical slice.
I guess you have a different definition of software architecture than I do. I agree that the problem isn't clean/onion/layered/etc, it's that the general understanding is about controlling coupling, but people also force that as a way to organize code. As mentioned in the entire video, it's not only about coupling, it's about cohesion.
@@CodeOpinion "I guess you have a different definition of software architecture than I do" Maybe :) I agree that it is about cohesion, the question is what are the criteria to decide if there's a cohesion or not?
I like how to say behaviours and capabilities and behaviours instead of "business requirements", which i hear quite often. Lumping these things into the "business" side of the work implies that developers have to wait for others to define those requirements, which now seem esoteric and non technical. Whereas it's just another way of looking at how the system behaves
This is one of my favorite videos of yours so far! The patterns demonstrated in the Clean Architecture sample that you showed are all too familiar, and I've tried in the past to get people to see why this is an issue. There are so many things to change when any use case changes, and it's super easy to miss something. It's unfortunate that so many people still cling to this file structure.
Thanks. This was talk I did recently at a conference so I decided to just record it. Really it's a collection of videos I've done in the past just mashed up together.
I feel validated by this video haha. I've had this discussion and demonstrated the separation of concerns alongside cohesion to many colleagues and I'm astounded how many are perplexed by the idea because "it doesn't follow the known patterns." Thanks for sharing this video!
In the past 3 weeks we’ve merged 4 micro services using clean architecture back into one of our main projects. We’ve started removing the clean architecture layers from those projects that have been merged in where it makes sense to. We are amazed at the results! We have gotten rid of so much superfluous code! We generally have 5x less code/files. The code is so much simpler (and cleaner, funny enough). It’s easier to find the places where to refactor… the clean architecture was hiding many places where we could improve our code. And the benefits of removing unneeded micro services has also removed superfluous connecting code. It’s been great! What seemed like an unmanageable nightmare now seems like it can be tamed and is now enjoyable to work with again.
How many people are working on each project?
@@cicerofoscarini8890 we’re a small team of 4 devs and a couple of external contributors. Usually there’s either one or two devs working on each project. Should never have gone down the micro services route as our team was too small to support it effectively.
Sounds like a monorepo was a better choice.
And that is exactly why this makes sense. Too many people are implementing without actually understanding why they are doing it. KISS is appropriate everywhere, or to use Einstein's methodology, "Everything should be made as simple as possible, but not simpler.". I think he captured it well.
Sounds like a decent decision. However please remember to update again here in 12 months id be curious to see how it pans out in the long term. There arent often golden bullets just different sets of problems and tradeoffs so will be interesting if you hit different issues.
Why not organize first into features, then layers? The problem with not making layers visible in the project structure is, that in a team with different skill levels present I'd say it is almost impossible to keep any unobvious layer concept intact over time. So you WILL end up with domain logic coupled to infrastructure and application concerns.
That's what code reviews and peer reviews should take care off 🤔
@@MiningForPies and architectural tests
There are always tradeoffs. The trouble with monolithic code patterns is that you often find similar usages across the vertical layers that require you either to couple one tower to another or else result in code being replicated across towers. I have seen the results of this pattern of not having a single idea of a product across business domains, and you end up with massive code replication and systems breaking routinely in production because one team subtly changed its definitions and business logic and this impacted other systems but was not caught in testing. The number of test cases that have to be written vastly increases and so forth.
In a real sense people are just pendulum swinging back and forth between different design patterns and as they get more familiar with one they start getting grass is greener on the other side issues, and start longing for patterns that were previously abandoned.
Whatever structure you go with, you still have to have a single representation of a determined concept in your code. In vertical slicing, what stops you to have kind of a "core" package that shared smilar code ?
You don't have to replicate the code; just extract it into another module and use that module everywhere it's used. And it should be sufficiently uncoupled with the modules using it to avoid future refactoring, just as its identical implementation in a layer would be. The only difference is now you're calling the layer in each module that needs it from within that module, rather than on all modules unconditionally.
Loved this video format with longer length, detailed content and nice pace. By far the best video of the channel so far.
Thanks. It really a bunch of previous videos mashed into one. Don't tell anyone 😂
Awesome content!
This is the best video about layer architecture I have seen lately. I believe people create templates with this module structure to enforce the prevention of compiling a module with a wrongly directed import statement.
Excellent explanation of something which should be interviewed for during every IT job interview, in my opinion, to filter out the pattern-as-dogma-crowd.
We use this organization structure at work.
One thing that can really help, especially with juniors, is something like ArchUnit (this is for Java). It helps to validate architecture, such as guaranteeing certain packages don't reference other packages, or even that certain named classes don't reference certain packages.
We have standard naming for standard purpose classes (e.g. *Repository, *Service, *Request, *Controller, etc), so we also make sure a class doesn't contain that standard name in the wrong package, or a standard name doesn't reference the wrong layer. E.g. *Service shouldn't access *Controller. This can be especially beneficial if you don't further divide things up in a vertical slice.
i successfully use NsDepCop for that purpose in a mid size project th-cam.com/video/rkU7Hx20Dc0/w-d-xo.html
Yes, I should of mentioned ArchUnit an similar, but totally forgot! Thanks for the comment.
That sounds very useful, I was worried about how to enforce the architecture and had never heard of packages that serve that purpose. Thanks for sharing!
The analogy to a recipe is perfect. Not everytime we need to do surgeries. We can do them sometimes in different zones. Not in the whole body.
Thanks for the amazing info and knowledge share. World gets better when we know what to do for us and clients.
Great video, awesome points and I mostly agree with all Clean Arch presented concepts!
I do have a point to add though, the dependency direction/coupling of the project exemplified at 5:44 seems wrong (or confusing) to me according to the Clean Architecture book's concepts (unless the Infrastructure layer shown is an Adapter layer called Infrastructure and not the real Infra layer which would be coupled with a specific tech choice).
That is, in my understanding, WebUI (blue layer/external layer/frameworks) must not reference an "infrastructure" layer (which should be blue, not green, cause it's also an external layer... the green layer is an adapter to the infrastructure where you must invert the dependency direction (DI)). A "purist" (by concept) clean architecture in this example (IMHO) would go:
WebUI ->| Application -> | Domain
_________| Application (I)|
It’s not actually a full vertical slice. In your example you are only slicing up the application layer but still leave other layers (i.e. infrastructure). Most examples I have seen also do this, but they don’t achieve what the commonly used image actually displays (slicing UI, application, domain and infrastructure).
Thus, you still end up with layered architecture in the end.
However, if you were to be consistent with the slice, that would mean plenty of code duplication, for instance, repositories would be sliced across features, and so forth, and that is not ideal.
"repositories would be sliced across features, and so forth, and that is not ideal"
Sometimes it's ideal, sometimes it's not. Sometimes features use different repository methods making it pointless to share. Sometimes repository methods try to do too much to serve the features that share it making it bloated and inefficient. One feature might need 2 pieces of order related data from one data source, another might need 50 across multiple data sources.
I always start by putting my repositories in the slices and then as the feature set grows I pull out what makes sense to share and leave the rest. If the shared resource gets bloated later on, I break it up to the slice level.
@@bike4aday I feel like there is a lot of exploratory work involved in Vertical slice architecture and we are still "getting to know it".
There are also mixed opinions and criteria of what should or shouldn't be the case with these. For example, if we dogmatically did it, with maximum feature isolation which does not leak out to other features, like in article "Out With the Onion, in with Vertical Slices", we also end up with *funny* things like css and FE code along with all other BE code.
You aren't going to have a DbContext per request. Nor would you have an aggregate per.. It would be for a feature set. The point isn't share nothing. It's putting related things as close together as possible.
I find the distinction easy to make.
Business processes/operations are self contained - they are the slice.
Loading data for a slice is part of that business process.
2 processes loading “the same” data is just coincidence. I won’t go too much into the pointlessness of using repositories with EF core ;)
“Infrastructure” is shared code. It has nothing to do with the business problem you are trying to solve. It is utility/convenience. e.g a “GitHubClient” which essentially wraps an “HttpClient” and exposes some specific methods to interact with GitHub/some 3rd party API
@@kabal911 It actually typically has a lot to do with the problem you are trying to solve, for example, Order entity is an infrastructure piece (DB/ORM). Same goes for Order router.
Probably rather than using infrastructure which confuses things it’s better to use “shared”
Just realized that GraphQL is CQRS by design (query and mutation). And using Apollo Federation and extending shared types is a way of slicing a common schema model and allowing each business area to add their specific view about e.g. a "Product", which is a cohesive architecture pattern by design. The only way to mess up coupling at that point is shared libraries which may still add coupling between different business areas.
I didn't talk about it at all in this video, but coupling usually comes when "needing' data between boundaries.
First off, thank you for rounding it all up in one video, I will spread it as far as I can, with this channel you're doing the code better! Could you make an addition to this video with practical examples? I still have so many questions on what do you do when you need to communicate between features, or creating a feature that depends on multiple feature, or how do you share the same model across multiple features? Thanks again!
Yes, I'll likely create something in the future to better illustrate
Great material. 😃 Looking back at the projects I've been involved in. I am also having similar thoughts.
I generally agree with this approach. However, it is important to remember that the entire team should have a similar level of knowledge/understanding of the subject. Especially people who are new to join such a project.
Yes. If people heavy into with layer by project, it can be a bit of a change.
😮 where has this video been all my life? I've just spun up another service using clean architecture in the classic form....thank goodness I've caught this early!
What about same application code being used by API, or gRPC or WPF forms should we put all of those “presentation” classes together? I mostly agree except with having the controller class bundled together.
This would exactly be the reason to have those in separate projects. Projects/assemblies are exactly for this purpose, reuse. So ya, don't put frontend with the rest of it.
nice new perspective in structuring...
the cake slices example and they way explained cohesion with it...
so beautiful.
it really makes sense.
wonderful
when using a single database, is it worth splitting the domain into smaller ones for each function, or is it worth using one super-domain project with a description of all entities? for example, if there are about two hundred related tables, when updating one of them, there will be a lot of work when updating its entity locally for each function. What is the best thing to do?
Separating by project/dll/package, is more about independant delivery/deployment and also team dependency, so it's better to separate by project/dll/package, trying to reason in terms of Dependency Inversion Principle (from SOLID) as suggested by Clean or Hexagonal Architecture. It's about seeing deliveries as PLUGINs for an independant package.
I think that a good way to think about coupling and cohesion may be the opposite as everyone does.
A single unit may have low cohesion (lot of responsibilities) but it has 0 coupling by definition because it cannot be coupled to itself. When you split that thing in two to make it more cohesive, now you got coupling.
That means that splitting things increases coupling and cohesion so they are opposite forces. It's an art to win in this situation and applying prescribed solutions doesn't help.
Also I see a lot of people that think that the mere act of splitting things reduces coupling when it's actually the opposite (e.g.: Microservices)
Yup, exactly. As mentioned it's a push/pull relationship.
@@CodeOpinion yes, nice video
1) This argument is somewhat similar to Uncle Bob's one, "screaming architecture", discussed in its book "Clean Architecture"
2) Using different projects in .NET enforces the acyclic dependencies principle, i.e., you cannot create circular dependencies. Without this division, you have to use tools like NetArchTest to have the same feature
3) For Web APIs, it is possible to use the REPR pattern (e.g. in .NET, Ardalis ApiEndpoints or FastEndpoints NuGet packages) to favor a cohesive structure of the files
4) The example of a product, discussed in the Informational and functional cohesion section, has been covered in detail in Mauro Servienti's talk "All our aggregates are wrong"
Thank you very much for your clear and thorough explanation! I prefer this longer format.
p.s. I'd really like to see a video with a list of recommended books!
*2) Using different projects in .NET enforces the acyclic dependencies principle, i.e., you cannot create circular dependencies. Without this division, you have to use tools like NetArchTest to have the same feature*
I've inherited a few projects over the years where this is most definitely not the case.
The only thing that overthinking architecture ever did for anyone is get solutions stuck in a dependency catch-22
What software architecture has taught me over the past 20 years is that software architects are addicted to solving non-problems.
In compiled DLL languages these problems that we are trying to solve are basically build into the DLL model itself. Basically projects: the architect’s favorite folder.
Completely made up doctrines like no upstream dependencies are primarily due to how compiled languages have separate DLLs requiring each over instead of just compiling the gdámn executables from the various dependencies.
The no-circular referencing of BLs is an OOP problem, to which the solution was always staring us in the face: less architecture, not more. Functions, not BLs.
The BL itself is a failure to do the basics right in the first place. KISS and separation of concerns.
The BL is itself an encapsulation of concerns, but a function isn’t. A function can have a dependency on another function that has a dependency right back on itself, and it’s fine. We even gave it a fancy word.
These same original DLL and BL problems just keep getting more and more fancy ways of solving the same self inflicted problems of too much architecture and not getting the basics right.
What do we do when one rigid BL needs to call another rigid BL that itself depends on that other one? We pull that functionality out unto a less rigid “helper”. And those helpers are just functions. See what we did there?
Now just take that same idea to the limit and get rid of the rest of the artificial, self-inflicted rigidity, and all of these fárt-sniffing solutions to the same old problems we created for ourselves go away, by OBEYing the fundamentals of software architecture.
1) KISS
2) SoC
Do these two things and vertical slices becomes an eldritch solution to a problem that no longer exists.
Then compile your in-house code to a (A) dll, and your upstream/downstream solutions become a “wtf are you talking about? What is upstream mean?”
It means that architects treat projects like folders, but to be fair, these problems are baked into the project based OOP programming models themselves.
Such a convoluted solution for a non-problem.
The motivation for vertical slices is always having to dig through folders to get all the files for an entity/feature.
Whenever I find myself doing this in layered architectures, I just use my editor to search for the filenames that contain the entity name and then open those. Problem solved.
Not really. What is the size of your codebase? How many different domains are mixed there? Do you need to migrate some of them at a time, or everything applies to all of them at once?
With a legacy solution, a layered monolith, how would the vertical slices be introduced into the same monolithic solution without fitting it into existing (non-clean) layers? Maybe a new project? Then vertical slices within that same project? Or a separate project or set of projects from each slice?
Thanks!
Thank you!
I do much agree with you, it's crazy. Thanks for all the context around every concept!
For anyone in the Node world and don't know NestJS, it has a wonderful ability to slice the onion so that you can build modules of feature functionality, which are like mini-onions. Very powerful!!!
The whole onion architecture is basically intended in the Node backend world with the request context being passed through a series of middleware functions. It's trivial to keep the style going from the middleware to the actual endpoint functions if you use typescript decorators, since they're just functions that wrap the function they annotate and are executed in the same order they're written. So you can have @Authorize @Validate @Whatever endpointFunction() { ... }, and it'll run authorization, then validation, then whatever and then the function.
Derek, your videos are just so good. You take the time to describe things I've spent over a decade learning in such great detail!
Thanks! Appreciate the support. Glad you enjoy them. Just trying to distill my own experience/knowledge in a concise way.
Great video, great presentation. Something was screaming "bounded context" at the back of my head all the time, though.
Your "features" are a level of granularity lower, but the example of "product" meaning different things to different users is how I understand "bounded context". Two use cases may be talking about the same physical entity but each views and handles it in their context, using their vocabulary and their features.
Yes, macro is a logical boundary (bounded context), within that features are at a more granular level.
Can you share a link to the GitHub Clean Architecture repo in the video
Not wanting to take away from what has been shared because it's awesome, as ever. As the video identifies, there is some value in cohesion along horizontals too. The domain is an obvious one, but the API can benefit too: HATEOAS is a love letter to cohesion across the top of the stack.
Excellent explanation of the concepts. Many of your points about the creation of turds I have seen created over time. The importance of the different contexts against the same data is critically important. Excellent video!
This is great. Having all the in-depth argument for this approach in one place is priceless. Thank you!
Thanks, Derek, very interesting video. I have a question, though. What happens when you have multiple clients that have the same feature but that show different data? For example, you could have a SPA, an MVC app and a mobile app that all need to display different item data. How do you do this? Typically I see people returning the full data with all the fields for each one, but if you only wanted to return the necessary data for each how would you achieve that? Would you add query parameters to the endpoint that specify which fields you need? But what happens if one of these clients need the items to be joined with another table/entity for additional data? I haven't yet found a good solution for this.
You're getting into BFF (Backend for Frontend) concepts with this and view/ui composition. I haven't done one exactly on this, but have talked a bit about it in here: th-cam.com/video/ILbjKR1FXoc/w-d-xo.html
I don’t see how this is really different to “traditional” architecture. You would still need separate/different endpoints if your REALLY wanted to support different data shapes.
The problem is not the code organization, but the endpoints/REST. This is ultimately what something like graphql tries to solve.
You might want to look at odata
I think this might be your best video yet. It provides a lot of context to how you think about architecture that isn't always clear in the shorter videos. I think a lot of those will make even more sense if I re-watch them after having watched this one. This is way more than just a video about project re-organization. It's about a better way of tackling problems (features) such that you use the best code for the job. Don't create a domain layer for a simple feature that only needs crud. Doing so will be the equivalent of wearing snowshoes in summer. You can still get around, but it's going to be longer and more complicated than without them. Likewise, using CRUD for a feature with complex business rules is like walking through deep snow in just your sneakers. You're going to fall down a lot.
Anyway, great job. Super helpful!
Ya, this video is really a combination of a bunch of videos that I've already done. Just makes the whole concept in a more cohesive (pun-intended) video.
That was like a lesson in the university, thanks!
22:20 i would prefer not to put everything in one file and rather make separate files in the same folder
why? because i dont like scrolling, and when i open a file i wanna see the thing it does.
i also place shorter functions/methods higher than the longer ones so i can see many thing without scrolling so much.
and time to time i open stuff next to next on the IDE and its not possible or hard if they are in the same file.
i also dont like nesting stuff into multiple folders deep, but i would just put everything into their own file in the same folder
I love the way you say "I'm not that way" .. its kind of implying that people with "those" beliefs are somehow misguided, but you're not making a value judgement out of politeness/repect
So kinda summarizing this I came to some conclusions. 1. You don't need to split small things into different files. Interfaces, validators etc. that belong together, should live together. Unless those things are shared across multiple components. 2. You need an API to access a component/module and that's the only entrypoint to it. Whatever happens inside that component is it's own business and it doesn't expose internal behavior. 3. Design your application by doing domain partitioning, not technical partitioning.
So vertical slices is about expressing the "virtual" vertical slices across a layered architecture.
My problem is that even you go great lengths to create vertical slice at some point the slices will share functionality with other slices. Excellent example is authentication and authorization, the functionality is the same for all slices. If you plug it into all or most slices it becomes a layer. At the end you do not want to directly depend on the data persistence solution, so you will build an interface and implement a service.
Slices without interacting with the domain are the definition of services?
One of the biggest advantages of layer architecture is that you can control what you expose to the next layer and also prevent spaghetti code. With just vertical slice, it's really hard to enforce decoupling and when working with a large team of developers people are going to mess things up. One idea I have in mind is having a Clean/Layer architecture and each layer is a separate vertical, trying to merge the benefits of both ideas
Understanding of coupling sure helps. There are ways to enforce a direction of dependencies beyond physical projects.
I had the same experience. Working with folder structures and in a pragmatic way was extremely difficult with large teams or teams with not enought seniority. In the end you need silver bullets for everything. Many projects to avoid coupling at compile time , mediatR to avoid fat controllers, even microservices just for damage control the devs etc etc.
Having said that... great video @CodeOpinion , love your work.
Grouping classes (that belong to one layer) in a single package, module or project has always been counter-intuitive for me. Just because a class has the "Controller" or the "Activity" in its name (as a suffix usually) doesn't mean you have to put all the controllers in the same holder (i.e. folder), as these classes have very little or nothing in common.
Instead, breaking down the application into the features (rather than layers) and then designing every feature as a single component/service, which contains many cohesive sub-components/classes, is a much more flexible architecture, if you later decide to, let's say, extract one feature as a micro-service or simply replace the libraries for event handling, messaging or even introduce a new DB. I am very happy this video recommends/approves this very same approach,.
One more thing, working on my bachelor thesis (ages ago...), named "The Cohesion in OOP", helped me to question the "layered" approach from the very beginning even though I had a very limited experience in software/code design, so I think it is very important to cover (or at least get familiar with) the theoretical aspects of the software engineering. It certainly helps (from my experience) to recognize and avoid the scenarios that could give you troubles in the future.
Great video! How do you feel about VSA on the frontend? I'm currently researching how to scale large frontend applications using design patterns, but a lot of resources only talk about the backend.
Do I have to implement Command & Query, Event Sourcing etc. to be able to make use of this vertical structure?
No. Absolutely not.
This is all fine and dandy, and you make some good points, points I wish more 'legacy' companies would consider when hiring people who actually WANT to make the systems architecture cleaner.
Unfortunately, the reality is many legacy companies (perhaps not the kind your example shows) operate on a "let's put all our business logic in the database, but never document it or track changes" mentality. This completely nullifies and obliterates any sensible architectural concerns (not saying MVC is good architecture, quite the opposite, but at least it can be refactored and tracked by subversion).
TLDR: Document your SQL in some way, or resign as a developer. I'm tired of having to guess what your complicated storedproc does because you spew a bunch of CAST()s and SUBSTING() without actually naming your variables (especially table aliases) something sensible and self-documenting.
There's also the "SQL is the way, the truth and the life" mentality, which is retarded.... there ARE other dbs out there and not all of them are trendy toys like MongoDB (Cassandra, Redis, and Neo4j are all exceptional at their jobs, yet I keep landing contracts with everything being stuffed into SQL Server like it's the 1970's)
How do you organize UI code, which often spans features?
If the UI is within the same platform, then next to it. For exmaple, if you're talking about MVC, all of them can be organized in code together. If you had a iOS app and a .NET Backend, that code won't live together from a development view, but it it is from a logical view.
So what’s the point in using MediatR in this example, rather than putting the logic in the controllers?
Your controllers should handle only the HTTP request-response cycle. Such as receiving inputs, calling services and return responses like succes or error messages. Your logic should be handled through a Service layer which get called in your controller. This is done for many reasons:
- Reusable: for example a UserService.EditUser(..) function can be called inside the ProfileController for users and inside AdminController for admins.
- Maintainability: If changes happen you only change the Service function and both controller endpoints will work right out of the gate.
- Testability: simulating HTTP requests to hit your controller endpoints during testing is annoying and difficult. It is much better to instantiate the service class, execute the function and then test it.
- Scalability: as your app grows you won’t lead to code inertia where it’s difficult to change or add new features. This separation will keep you to ship rapidly. MediatR is excellent for keeping controller functions at max 3 - 4 lines of code and transfer all your business logic to separate Service layers
If you're optimizing for number of files opened by the developer you can always put your entire code in one file :P
ha. I've heard of people actually doing this. Don't recommend. I do recommend as I mentioned when files length still relatively small.
Awesome examples to really pin down the argument. Great job.
I’ve followed vertical slice architecture on a very large project (with the nested class for naming) - one thing we don’t do is the controller per feature which is an interesting idea. We have a partial controller for each entity, with a another partial for the actual feature route. I think I like your way. Not sure what impact it would be moving from 10 controllers with around 500 actions, to 500 controllers?
Good question. If anything, startup time of it discovering all the controllers. But how impactful that would be, unsure. You'd have to benchmark it.
You said that we got rid completly of layers ,but what happened with the domain model and infra layers?Thank you!
thank you for this explanation, but I had some thoughts in my mind regarding this
as I know clean architecture is not just meant for cohesion but also meant for isolation between business logic and infrastructure, in clean architecture you need to build some sort of abstraction layer using facades and adapters to avoid technology changes, so for example if you wanted to change the database, you can build the actual implementation that matches the adapter layer and here you go you changed your infrastructure without affecting other important areas, but in vertical slice architecture, while you are grouping related things together, if you wanted to change your db to another one, how much change should be done?
This is brillant I wish I had the experience and knowledge you have :) thanks for the talk
Glad it was helpful!
Wonderful video!
I love it.
I have a question. If I share a model between features, I share property and behaviour that don't interest all the feature.
Is there a way to solve it?
Example a shared basic model only with SKU property and treats with property and method, in this way I can share logic, I can build custom model for each feature.
The same for repo. I don't delete method available on update feature.
(I understand tha I can execute a simple query instead a orm function.)
Is this a good way in your opinion?
You don't have to have a "model" that's shared at all if you want to simply use a transaction script. I only suggest it if you don't have consistency concerns and it's really just a data model. At that point your transaction scripts are implementing the behavior. If you do have a lot of logic, complexity, and need consistency, then stick to an aggregate. Check out: th-cam.com/video/aLFMJ_frafg/w-d-xo.html
How do you do domain validation in a reusable way within this architecture? This would be outside of your normal validation making sure this is a valid email. Something along the lines of ensuring the email is unique or more complex scenarios where you have to do a DB call to validate this is a valid action given the persisted settings.
Interesting one. Do you think, we should have seperate projects for logical concerns, rather than projects split into clean structure like adding domain, application etc? Could these be folders in a single project, for vertical slice?
You can, but you don't have to. Projects are often just use as a physical barriers or to prevent circular referencing. You don't need multiple projects at all. You can just use a folder structure within a single project. All depends on what your needs are.
Whats the difference between coupling and cohesion?
Glad you asked, check out this video. th-cam.com/video/YDNR_gfBk0Q/w-d-xo.html
I wrote a Blazor app for work a few years ago and followed the Clean Architecture structure. I've been on the hunt for a good way of structuring apps for years, and I tried it out for this app. As I was developing it, it kinda made sense. Then I deployed the app and went away for a while, doing other work. Then I had some change requests and bug fixes for the app. And looking at it now, it is a complete mess! Exactly as you describe here - files in so many different places across several projects, so making a simple change can involve changing ten different files.
At some point, I'm definitely going to rebuild the app and base it on a much simpler structure. VSA might well work for that. It'll certainly be a lot better than what I have now!
Yes yes and yes. Started my first job as a junior feeling ready to make some nice layered code with nice abstractions and it looked awesome when i was done with the initial codebase, only to absolutely hate whenever people asked for new features / changes, because i had lost the overview. I was afraid of changing stuff as the use cases changed
Lately i've been grouping stuff based on ownership even if that called for some duplicate code here and there, but it guaranteed me i knew what would be affected whenever i went in to make changes.
Been waiting for this one! Will enjoy listening in during lunch tomorrow :)
Hope it's everything you thought it would be! 😂
@@CodeOpinion was great! Though it raises a few questions.
If you do sliced architecture and need some background jobs like azure functions. How we do it is sharing libraries between the api and the function app to make sure both are deployed in case of underlaying changes. In those cases we actually end up with some middleground where we make a Core library that has shared data models and infrastructure to avoid having to duplicate models and forget to make changes.
I'm also interested to see how a VSA would look without MediatR. I guess you would end up building something similar?
When you put controllers in same file sliced out, how do you deal with shared routing eg. api/products... do you put that as static constants? Have you tried using minimal apis with this approach or the nuget FastEndpoints?
I'm also all for putting multiple classes in the same file. This is common when doing frontend do why cant we do it in backend? :) i have managed to change the mind of one in my team, so now we do it hehe
@CodeOpinion may I suggest this is very much the idea (loosely) of how Unix handles things via scripting together a bunch of relatively simple utilities: not all of them have I/O other than standard I/O and stderr, nor do they need to: they may instead set the system to a particular state instead as they have data filtered through them, or any other arbitrary number of things.
It’s true, it’s not a perfect analogy. When building up complex systems of scripted components like this in processing pipelines, it’s easiest to do it incrementally along the way. Ultimately an “application” is just a bunch of simpler processes chained via the script. I’ve for many years considered this is likely a better way to do things within a single process instead of the Unix method (or using PowerShell, which is more OO) of composition by smaller programs.
I’d suggest a great advantage of doing things with these smaller vertical slices however it is done, is it isn’t as painful to obliterate something you wish to remove or replace, in all aspects: all code is easier to delete, and code that is easier to delete is easier to perfect, where the ultimate perfection is no code at all: that’s one bit of code you can be sure won’t be buggy 😉
Amazing video and concise examples, nice work! This is a common topic and all because IDEs don't allow the creation of tags and scopes for project structure visualization. I'm currently working on an extension that allows you to create your own project layout views using annotations. Then arrange your code and files as u want ;)😉
Great video with a lot of detail. But, one file having the whole slice/feature in with potentially 100s or 1000s of line long sound terrible, most likely would have regions all over it. One reason why layered/clean architecture is so popular is everyone knows it and understands it, when working with external developers this is beneficial as its something everyone can relate to.
Generally, these files don't end up large at all. In my experience, around 200 lines give or take. And no, you don't need regions 😂
Introducing and selling vertical slicing is not too hard in my experience. Developers get it instantly, including external developers and fresh from college juniors. The benefit of not having any significant merge conflicts when working in parallel (feature/developer) is a big selling point. Other architects are harder to convince, like always.
Multiple classes in the same file is code smell to me, even if IDE's allow you to find it. It makes it harder to modify than necessary when you don't have it collapsed when scrolling around... Especially if they have similar looking code. There's a reason why classes are per file. I agree with DDD though and organizing by logical/feature-based layers. Microservice architectures basically lend themselves to this design strategy.
At 8:11 would be really helpful for random access viewers to label bottom as “business domain” … one of these is not like the other. (If that’s the case, not an msft person)
Very nice video with a clean explanation, I like it. However, one thing still bothers me; why did you put the controller into the vertical slice? Shouldn't it be at the edge, like persistence? Or another way around, shouldn't you also put the persistence into the same vertical slice?
Persistence was shared several requests, hence why it was directly in the same file. The controller with the single route was in the same file because it's specific for that request.
Great video, it's a great structure for larger projects. I'm having one issue though, and I can't find where in my configuration I'm doing this wrong - When Nesting the items under a static class (22min in), it all works EXCEPT for the controller. As soon as I put the controller inside the static class, it fails to find and expose it.
Any tips on making this work?
Why is your controller nested inside a static class? It can be in the same file, it doesn't need to be wrapped in a class.
Excellent video. Very well explained. Thanks.
At first specifications looks nice but then you realize they are a very limited overhead over a dbContext that is already an abstraction of the database. I loved how you grouped your features and files with multiple classes per file.
Excellent explanation of concepts. Thank you for making this video.
Glad it was helpful!
This has been a consistent approach I have utilized for more than 15 years. I probably should have coined it sooner. Thx for the video.
Absolutely great and helpful video! Thank you, Derek!
Glad it was helpful!
Can you make a video? Over migration from relational to no sql. Taking reference of schema you shown at 23:00. What need to taken care. When need to embed, when we should go for relationship etc. I would love hear those key points.
Interesting suggestion!
Good points. But if these layers aren't physical separated, you will have people start directly referencing infrastructure code in your UI code. Then we're back to the 90's. There's nothing wrong with that, if your app is small.
That ☯️ thing explains it all. It's a balance and it depends.
Awesome video, thanks. I wish this design was more broadly used, in projects with frameworks like Rails it's really hard to see it in use
Excellent video Derek! I still have lots of question regarding Vertical Slices, but I'm getting to it...
What's your opinion regarding that issue:
Let's say I have a use case that involves 4 changes to the database on 4 different Services, one depending on the other to get the ID for example. These changes are within methods and they call other repository methods. How can I mitigate issues if something fails between one update and another? Rollback is not an option since I am working with different Contexts.
Workflow. I have a bunch of videos on this, but first top of mind: th-cam.com/video/hGiPWfWG8gs/w-d-xo.html
This also doesnt work if you work on a large application that needs to be distributed as nuget packages. Think Hangfire. It has support for different backends and infrastructures. Imagine if it distributed one assembly that had Postgres, Sql, Sqlite, Redis, RabbitMq etc etc. The huge dependency surface of adopting that into your project.
Yet the cohesiveness argument says it would make sense to have say the abstraction for starting a hangfire job, and all of the implementations of that close together in the same source file according to this approach. Its not even an option its a non starter there. You cant even put those implementations into the same projects they have to be factored into projects per system you are supporting so you pass on the minimal dependencies to consumers in the name of stability.
I think I know where you going with this but view it a bit differently. Hangfire is a library that contains abstractions for persistence, vs what I'm talking about is application code that isn't cross cutting at all. Its specific to a feature, there isn't any abstraction. If you do have cross cutting concerns or shared types, (eg: events) those absolutely would be in a different project that could be shared via a nuget package. The point isn't to 100% have everything sitting right next to each other, it's to have code organized what can be together. What changes together lives together.
@@CodeOpinion yeah I agree there are two different paradigms here though. For the application case it makes sense because you aren't passing on implicit dependencies with the way you structure the code - no one else is "pulling" in your application to use in their scenario. This is unlike all the second paradigm which is more for things packaged up and distributed specifically as dependencies.. Maybe this is obvious; but just interesting for me to call out as someone who works in both spaces it would hint that there is no "one best way" of structuring the code. Then again if the project and build system was more capable perhaps indeed both of these paradigms could use the same approach and something in the project / build system would do the appropriate splitting to seperate assemblies / packages etc.
Very detailed and good explanation about Vertical Slice Architecture. Thank you!
One of the best, I would not say video but guide, arround Internet about SW Architecture. You touched some wounds over here (like, c'mon.... have you ever read the name of the channel?).
Thanks. It's all intended as food for thought.
Can you provide a link to some github repository showing this approach (vertical slices with e.g. clean architecture)?
I'll be covering it in a video sooner rather than later that has a more concrete example.
Yes!!! nothing wrong with multiple classes in a file, had discussions on it... i like my interface right above my class in the same file. If i have 2 implementations of an IStorageService, one for local file storage and the other for writing an reading from a blobstorage, i like them in the same file. So glad i am not alone on this.
I disagree with this one. 🤷🏻♂️
@@rowser4472 without a sound argument against having an interface close (in the file) to the class that implements it is has no ground.
@@rowser4472 Why?
My thoughts:
Clean Architecture, as prescribed many for .NET, is hell in a microservice system. Too complex. I can tell you that. But I get why people like it: For the project boundaries and the direction dependencies. Just don't use it for microservices!
About the "microservices thing", a lot of developers are going to microservices because "that is what they should do", putting parts in separate projects/apps without the actual need or requirement to do so. "Microservice per table" is a great example when you shouldn't build microservices. Perhaps you should wait and get some experience with them outside a professional project, at least. As you show, there is more to it than just divide tables across projects.
For those in the back: "there is more to it than just divide tables across projects". 👍
I have to say thats a fine piece of cohesive cake.
Do you have this code sample on git?
No. There really isn't much to it. I just moved files around for the most part from the original eShopOnWeb.
Always great. But I have a non related question, my work experience is 10+ years. Now I want to become an architect. What will be starting point and path for me? Please suggest. Thank you!
Here's some advice I'd give to my younger self: th-cam.com/video/pSMXDfRfyEc/w-d-xo.html
I would like to know your opinion on how you would deal with the following scenario. Let's say we have an API project where we implemented features using Vertical Slice Architecture. All these features(slices) are implemented in the Features folder using the illustrated pattern from the video. Now we decided to have Blazor Webassembly as our frontend and we would like to reuse requests and responses(commands/queries) from the API project. What would be the most appropriate way to share these requests and responses? Would it be okay to create a DLL library(contracts) that would mirror our Features folder from the API project and declare there only requests and responses(commands/queries) and then we would reference this DLL from both API and Blazor projects? With this approach, it would be theoretically enough then to implement those queries/commands in the API project and we should be able to reuse the same code between the frontend and backend. Or is there any better approach for this kind of code sharing? Thank you
Yes, do that. Third project with the models you need shared.
Longer length of video is great. Liked it Derek.
Thanks I'll try to throw a longer video up here and there. This really is a bunch of my previous videos over the last couple years mashed into one.
What is the reasoning behind having just one controller per feature?
Any changes to your feature, are isolated to this one controller. In other words the controller class will have only one reason for change.
Thank you. The content of your video is very practical and pragmatic.
Glad you think so!
Ui can be also web, mobile, desktop and so on, I do not care about UI too as long as it can reside on the same APIs.
Excellent, really apreciate u time. Very useful.
Thank's for this Derek. Is there a template such as the one for Clean Architecture for this approach? It's always much easier to start doing stuff in a different way when there's an example how this is done. If the template gains popularity people will most probably be willing to contribute to keep it up to date.
I plan on creating another video that better illustrates some of the structure. Won't likely be a template but should get more of the gist across.
Loved this video, I think this is a really underrated topic.
I'm not sure I agree with the framing that increasing cohesion increases coupling and vice versa. Take the reddit guy's data model as an example. If you were to stick whatever that app is into a typical layered structure but without some kind of vertical/feature slicing, you would end up with a ton of coupling caused by the center of the layers having very little separation of concerns. All the features would be extremely brittle to change in one another. This feels like coupling to me.
I think maybe a more accurate framing is that high cohesion creates greater local coupling in exchange for the benefit of lower project-wide coupling. The intra-feature bits that do cohere with one another will be slightly more coupled - which costs much less because of their natural relatedness - but the rest of the project becomes significantly less coupled to each individual feature, which is a far more expensive kind of coupling.
Curious what you think about that.
Well said. I'd agree with that.
Doesn’t StyleCop forbid multi-class files? Silly rule but lots of devs are stuck with it.
With this line of reasoning, all the tests should be in the same files also or if not the same files then in a file next to it. This might mean taking a dependency on xunit which accidentally gets deployed with your applications - this sort of thing can easily cause unexpected issues - when you see xunit assembly appearing in the browser of a blazor wasm app :-)
I actually am really attracted to this, I just dont think our current project systems are set up for this. You'd want to set safety rules to enforce the layers and accidental misusage between classes. You'd want to exclude all test source files from being compiled into the binary when released for deployment but typically we do want to build the binaries in release mode once and test the same binaries that get deployed through to production so I see more problems there. Projects for layers arent as nice for cohesiveness but they are a compromise offering a level of ingrained safety through rigidity.
I absolutely wish and have said many times, I wish tests could live right along side the code your testing. If there was a meaningful way in .net to do this because of packages etc and not have them shipped/built in output would be great.
@@CodeOpinion sounds like a feature request is needed to Microsoft. Could be very innovative, put them ahead of other IDE's..
Hey Derek
Thank you for the great content, i was messing around vertical slice arch for a while now and i really enjoying it. But there a question, if the controller and command and command handler are basically in the same file why do we need mediator then?
You don’t… see minimal APIs
Something like MediatR separates ASP.NET Core from your application code. You're converting an HTTP request into an application request. You don't need MediatR to do this, but it forces the concept. However, if you were to put HttpContext or anything ASP.NET Core into your requests, you're losing some of the purpose. I find the true benefit if you're creating a request pipeline for your application (mediatr) request. There are other libraries that do this beyond mediator that also work out of process (wolverine, brighter, etc).
Most of the time you don't, especially for small services its not required.
Great stuff - thank you!
Vertical slice is not an architecture. Clean architecture is not the a problem but people understand that clean, onion or whatever architecture are application architecture like MVC is the problem.
Clean architecture is a bound-context architecture so I think vertical slice is just a renaming of bounded-context, bounded-context is even better since it gives criteria (DDDesign way using ubiquitous language) about what to put inside the boundaries.
FYI in the clean architecture book there's a chapter with title "The missing chapter" written by Simon Brown where we can find how to package modules in different styles and end up with package by components which is another name of vertical slice.
I guess you have a different definition of software architecture than I do. I agree that the problem isn't clean/onion/layered/etc, it's that the general understanding is about controlling coupling, but people also force that as a way to organize code. As mentioned in the entire video, it's not only about coupling, it's about cohesion.
@@CodeOpinion
"I guess you have a different definition of software architecture than I do" Maybe :)
I agree that it is about cohesion, the question is what are the criteria to decide if there's a cohesion or not?
Cohesion has a definition. I show it in the video, specifically informational and functional cohesion.
I like how to say behaviours and capabilities and behaviours instead of "business requirements", which i hear quite often. Lumping these things into the "business" side of the work implies that developers have to wait for others to define those requirements, which now seem esoteric and non technical. Whereas it's just another way of looking at how the system behaves
Thanks! Spot on.
C# slowly moving towards idiomatic Rust practices ;)