Тёмный

The missing Project that fixes everything in .NET 

Gui Ferreira
Подписаться 13 тыс.
Просмотров 10 тыс.
50% 1

Most .NET Clean Architecture and Hexagonal Architecture implementations have this problem. The problem of breaking the dependency rule. Here we will see how to fix it by implementing the Main Component, or Composition Root.
🔗 BLOG POST: gsferreira.com...
🚨 KEY LINKS
🤝 Support me on Patreon (and get access to source code) here: / gsferreira
👋 HEY FRIEND
If you're new to my Channel, my name is Guilherme, but you can call me Gui if Portuguese pronunciation is not your thing.
I see myself as a Minimalist Software Craftsman. That says a lot of what I talk about here.
So, why this RU-vid channel? To share with you to simplify your life as a Developer through knowledge, technology, and practices.
If you are into those things as I do, don't forget to subscribe for new videos.
🔗 GET IN TOUCH
LinkedIn: / gferreira
Twitter: / gsferreira
GitHub: github.com/gsf...
Visit my blog: gsferreira.com
#cleanarchitecture #hexagonalarchitecture #softwarearchitecture #dotnet

Опубликовано:

 

14 окт 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 65   
@MilanJovanovicTech
@MilanJovanovicTech 2 года назад
Really good video. I learned something useful here!
@gui.ferreira
@gui.ferreira 2 года назад
Glad to hear it Milan!
@EliGassert
@EliGassert Год назад
I like it overall. I think that there needs to be some kind of "information leak" from the adapter layer up to the main component layer. What happens when the API layer requires a 3rd dependency? What happens if that's a separate team maintaining that part? When will that failure be discovered? Before calling your StartAsync you can perhaps have a Verify or Assert function call on your adapter, whereby it checks itself for proper configuration (in this case, it would ensure that _someone_ registered the required injections). Another way (not mutually exclusive) is to use the actual Options pattern to have properties that the user should fill out that will make the configuration complete. e.g. options.SetBookReadStoreType() where T : IBookReadStore. In this case, the .NET DI pattern is being used almost like the Builder pattern. Another interesting project (haven't used it personally!) is something like StrongInject which can generate compile-time errors if a required injection isn't registered. Like I said, I haven't used it, but I could see it being really useful in a composition situation like you presented in the video. Great stuff! Thanks for sharing
@gui.ferreira
@gui.ferreira Год назад
Using something a bit more "structured" as the builder pattern might be a good way of addressing that. 🤔 Thanks for sharing your thoughts here
@codewkarim
@codewkarim Год назад
That's very intersting, thanks for sharing. I'm not fond of having an API depend on my Infrastructure just to register its dependencies. I was thinking to refactor my project to use something similar, so your video is on point :D
@gui.ferreira
@gui.ferreira Год назад
I'm glad that you have found it at the best moment 😉
@ChyK24
@ChyK24 9 месяцев назад
Nice example of using "Dependency Inversion" (D from SOLID) to decouple components (Adapters here) from each other.
@gui.ferreira
@gui.ferreira 9 месяцев назад
That's true. The great ideas in software development tend to gravitate and build on each other.
@90tiagosilva
@90tiagosilva 2 года назад
Great content, very clean, thanks and keep the goodwork👍
@gui.ferreira
@gui.ferreira 2 года назад
Thanks for taking the time to give feedback, Tiago! It means a lot to me.
@AboutCleanCode
@AboutCleanCode 2 года назад
Interesting approach - I will think about it for my next project
@gui.ferreira
@gui.ferreira 2 года назад
Thanks! Let me know how did it go.
@ebrahimmansur9815
@ebrahimmansur9815 Год назад
@@gui.ferreira nice video, if you only want to separate your controllers and endpoints into a different assembles we can use the [ AddControllers().AddApplicationParts()] and pass in the types we want to use from our class library's which hold our Controllers, filters, and any thing we want to register. and remove the controller,views and other folders from the root project and move them into the class library.
@juanmateorodriguezsanchez868
@juanmateorodriguezsanchez868 9 месяцев назад
Hello! Nice video! I have a question... Where should appsettings live? In the adapter project or the composition-root project?
@gui.ferreira
@gui.ferreira 9 месяцев назад
Hi! I use them in the composition root
@antan87
@antan87 8 месяцев назад
Great video! Would be interesting to see how you would configure an real implementation of a orm or message service.
@mauroprogramador4486
@mauroprogramador4486 Год назад
Muito bom ! Parabéns ! Vou usar isso nos próximos projetos
@gui.ferreira
@gui.ferreira Год назад
Obrigado 🙏
@javidmohajery8268
@javidmohajery8268 Год назад
Really useful video. Thank you Gui
@gui.ferreira
@gui.ferreira Год назад
Glad it was helpful! Thanks, Javid!
@Eamo-21
@Eamo-21 2 года назад
nice job Guilherme! Do you always use JB Ryder ?
@gui.ferreira
@gui.ferreira 2 года назад
Yes Eamon. Sometimes I use VS Code as well. Especially if I know that I will only be reading code.
@mauroprogramador4486
@mauroprogramador4486 Год назад
I did this way and works fine for me! The only thing that I cound't understand is how make WebApplicationFactory class and ConfigureWebHost method works for integration tests with this architecture. Maybe it could be a nice topic for another video :)
@sdecoodt
@sdecoodt 10 месяцев назад
I was wondering as well how good this approach works with the WebApplicationFactory. If this pure approach ends up being less testable then I can't call it a win. :)
@jonasupeterson
@jonasupeterson 12 дней назад
How would you register entityframework, which has a ApplicationDbContext class in for example Persistence project, but also need IConfiguration?
@gpzim981
@gpzim981 Год назад
Excellent video, this was always a problem that bodered me. Question: Are you able to run this project on VS or VSCode? Here I am getting a Failed to determine the HTTPS port for the redirect
@gpzim981
@gpzim981 Год назад
Ok, I found the problem. It was losing reference for the launchSettings.json because it was inkoked by another project in a different path. I had to: Directory.SetCurrentDirectory(dir); var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { EnvironmentName = "Development", ApplicationName = "WebApp2" });
@gui.ferreira
@gui.ferreira Год назад
Nice one! And thanks for adding it here in case someone else faces the same problem.
@purplepiranha
@purplepiranha Год назад
@@gui.ferreira I've just got around this issue by changing the entry project to use the Microsoft.NET.Sdk.Web SDK. However, I now have another issue. Nothing from the wwwroot folder is served. I can't see the previous reply so not sure if that would help me.
@gui.ferreira
@gui.ferreira Год назад
Hi Paul, I've updated the source code (accessible to Patreons) with a configuration example. The adapter needs to configure the ".UseStaticFiles()" with the path. By default, asp.net assumes that static files are part of the entry point. var contentPath = _app.Environment.IsDevelopment() ? Path.Combine(Directory.GetCurrentDirectory(), "../Adapter.Api", "wwwroot") : Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "_content", "Adapter.Api"); _app.UseStaticFiles(new StaticFileOptions() { FileProvider = new PhysicalFileProvider(contentPath), });
@purplepiranha
@purplepiranha Год назад
Thanks Gui. That works brilliantly. I do like the idea of abstracting the underlying implementation from the presentation layer in this way.
@ilovepandaypoe6056
@ilovepandaypoe6056 Год назад
how would you deploy this in production? what kind of app service would you create?
@gui.ferreira
@gui.ferreira Год назад
I've been doing it by deploying a docker image to the Web App Service However, take a look at the comment section. You will find there that others have deployed it in other ways.
@sonederbr
@sonederbr Год назад
Really interesting. Top!
@gui.ferreira
@gui.ferreira Год назад
Thank you! Cheers!
@susantamaji2941
@susantamaji2941 Год назад
Greate content, Thank you so much for this content. But I have a question... How to deploy this application in IIS. Because console app doesn't work in IIS.🙏🙏🙏
@gui.ferreira
@gui.ferreira Год назад
Hi Paul, I've updated the source code (accessible to Patreons) with a configuration example. You can update the csproj from the root project to: Instead of
@susantamaji2941
@susantamaji2941 Год назад
Thank you sir.
@alireza136211
@alireza136211 2 года назад
very informative video, thank you
@gui.ferreira
@gui.ferreira 2 года назад
Thank you 🙏
@johncerpa3782
@johncerpa3782 2 года назад
Excellent video
@gui.ferreira
@gui.ferreira 2 года назад
Thanks, John 🙏
@iliyan-kulishev
@iliyan-kulishev Год назад
Getting back to this video after some time. So the point here is to prevent infrastructure projects referencing each other by introducing one "dirty and all knowing" project that acts as Composition Root. Following this logic, I wonder why not having these constructors just in the Composition Root - one place that knows how to build, register dependencies and run the projects.
@gui.ferreira
@gui.ferreira Год назад
Hi Iliyan, When you say "having these constructors" which constructors are you talking about?
@iliyan-kulishev
@iliyan-kulishev Год назад
@@gui.ferreira In your example that would be the ApiAdapter constructor. The project Application.Api which acts as composition root is using that. I was wondering if Adapter.Api or any other project should provide such constructor at all or let the logic for DI/build/run for each project be defined in the composition root itself.
@gui.ferreira
@gui.ferreira Год назад
​@@iliyan-kulishev Now I get it. My approach is to keep the adapter self-contained and only expose a way to be configured (DI or other settings). You can do that through a Contractor or through a method. In that way, Composition Root only has one responsibility, configuring adapters and starting them. Let me know if my answer isn't clear.
@louisgerard3916
@louisgerard3916 Год назад
Have you ever succeeded in deploying (to Azure Web App for instance) ? It actually doesn't look like serving any request
@louisgerard3916
@louisgerard3916 Год назад
Got it working by manually creating the right web.config
@gui.ferreira
@gui.ferreira Год назад
Really? I've been doing it by deploying a docker image to the Web App Service. How are you deploying it?
@louisgerard3916
@louisgerard3916 Год назад
@@gui.ferreira I think it works OOB in your case because you are specifying the Dll as the entry point in your Dockerfile
@gui.ferreira
@gui.ferreira Год назад
Makes sense. Did you manage to sort it out? Should I take a look?
@louisgerard3916
@louisgerard3916 Год назад
@@gui.ferreira There probably is a more automated way to have the publish step to create it, but I didn't investigate yet. Here is the web.config:
@rafaelbatista_yt
@rafaelbatista_yt 2 года назад
Olá, Gui. Como estás? Como eu faço para adicionar/instanciar no Application.API outro adapter. Pois ao fazer isso, o método RunAsync do segundo adapter não é invocado porque o primeiro fica segurando a execução. Ex: Criei dois projetos de adapter (Adapter.API, Adapter2.API), cada um com suas respectivas controllers, mas só consigo invocar um deles no Application.API. Grato, meu caro.
@gui.ferreira
@gui.ferreira 2 года назад
Olá Rafael! Tudo bem e por aí? Para aguardares por ambos, basta passar as duas tasks para um Task.WhenAny. Tens abaixo um exemplo, onde modifiquei apenas o Application.Api do video. Mas, posso perguntar qual é o cenário que queres montar? ``` var api = new ApiAdapter(args, options => { options.AddScoped(); options.AddScoped(); options.AddScoped(); } ); var api2 = new ApiAdapter(new[]{"--urls=localhost:5005;localhost:5006"}, options => { options.AddScoped(); options.AddScoped(); options.AddScoped(); } ); var tasks = new[] { api.StartAsync(), api2.StartAsync() }; await Task.WhenAny(tasks); ```
@rafaelbatista_yt
@rafaelbatista_yt 2 года назад
@@gui.ferreira Sensacional! Funfou! Obrigado pela resposta e pelo ótimo conteúdo apresentado. Grato, meu caro.
@gui.ferreira
@gui.ferreira 2 года назад
@@rafaelbatista_yt Boa! Keep watching 😉
@0i0l0o
@0i0l0o 2 года назад
Nice.
@ebrahimmansur9815
@ebrahimmansur9815 Год назад
@gui.ferreira Nice video and very interesting, is there a repo i will love to check it out, if you only want to separate your controllers and endpoints into a different assembles we can use the [ AddControllers().AddApplicationParts()] and pass in the types we want to use from our class library's which hold our Controllers, filters, and any thing we want to register. and remove the controller,views and other folders from the root project and move them into the class library.
@gui.ferreira
@gui.ferreira Год назад
Hi Ebrahim! Unfortunately, I'm sharing the source code yet. As I pointed out, I'm not a fan of just moving the controllers to a different project. Because when we do that we split the web project in two, without ensuring that the dependency rule can't be broken by other web concerns like Filters or Middleware. It works, but you need to be extra careful with it.
@ebrahimmansur9815
@ebrahimmansur9815 Год назад
@@gui.ferreira I see, my goal was to breakup the problem space by having the root project act only as a host and invoking the dependency rules placed by each module through extension methods, where each module will handle a specific subdomain , and again I really your playlist.
@gui.ferreira
@gui.ferreira Год назад
​@@ebrahimmansur9815 that is absolutely valid. I would say that the video that I presented goes more in the Composition Root direction.