Тёмный

Highly COHESIVE Software Design to tame Complexity 

CodeOpinion
Подписаться 84 тыс.
Просмотров 13 тыс.
50% 1

What is cohesion and why should you care? Highly cohesive software design can reduce complexity and coupling. But what is cohesion? It's the degree to which the elements inside a module belong together. How you group operations together can have a widely different outcome on Cohesion. Informational Cohesion is grouped by operations on data. Functional Cohesion is grouped by operations of a task. It's directly related to the Single Responsibility Principle, which you might also have a different definition of.
🔔 Subscribe: / @codeopinion
💥 Join this channel to get access to source code & demos!
/ @codeopinion
🔥 Don't have the JOIN button? Support me on Patreon!
/ codeopinion
📝 Blog: codeopinion.com
👋 Twitter: / codeopinion
✨ LinkedIn: / dcomartin
0:00 What is Cohesion
1:25 Informational & Functional
4:55 Code Example

Наука

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

 

11 июл 2024

Поделиться:

Ссылка:

Скачать:

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

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 56   
@CodeOpinion
@CodeOpinion 3 года назад
Do you primarily think about cohesion in the sense of informational cohesion or functional cohesion?
@qizhang5749
@qizhang5749 3 года назад
Primarily informational. I start with a model of the database tables and how they interact. Usually because I know what data I want and need but not how they would be used by different parties.
@alivateRocket
@alivateRocket 3 года назад
Informational Cohesion, that's what is custom and specific. Within Data, informational cohesion is inescapable, all data is relational/graph. I believe it's possible to remove "implemented" informational cohesion completely from custom lines of code. You still need documentation and planning around it though of course. I understand "PostgreSQL Database Driver and SQL dialect" to be a kind of Functional Cohesion, an essential general tool that you need, just like the operating system, and the programming language - they are all generalised and flexible. I believe the number of Functional Cohesions should be minimised - you shouldn't have PostgreSQL, Redis, and ElasticJS - at least not to begin with on a smaller project - you should start by supporting one operating system, but with tools that can take you further if you need to.
@markuss.6981
@markuss.6981 3 года назад
Informational cohesion is probably a starting point when creating a new feature as the tasks the feature provides will show up in a later stage when you are extending the feature. Often people just blindly follow the cohesion pattern that is already there from an earlier stage of the feature / software and just keep extending those services bounded by informational aspects. Constant refactoring towards achieving functional cohesion should be the goal imho.
@BertrandLeRoy
@BertrandLeRoy 3 года назад
Every time you remove mocking code from a test, it’s likely you’ve improved your architecture. Very nice as usual!
@CodeOpinion
@CodeOpinion 3 года назад
Very true! Thanks!
@andrewthompson9714
@andrewthompson9714 Год назад
Absolutely 100% this
@pawetarsaa9904
@pawetarsaa9904 2 года назад
I have the impression that the problem you solved was just migrated to higher level. I believe there is still a need for this mock test but on the function that calls the handler since the implemenation of this delegate is still required
@hpdipto
@hpdipto 2 года назад
"When I'm thinking about cohesion, I'm thinking about functional cohesion. Not informational cohesion." -- precisely clear!
@LeonardoDias-te2gg
@LeonardoDias-te2gg 3 года назад
Great content! I always thought this idea of having a service per entity that does a lot of things was a bad idea. In your example, you have only a few methods, but I have seen "entity services" with a lot of methods and large methods. One thing I love in CQRS is that if I have a "Create Product Command" it only needs to change if the business rules for creating a new product change. But if I had a ProductService with lots of methods, this class/interface could have many reasons to change(for example if I change the rules for another method but the CreareProduct). The delegate approach you show in this video is very interesting, I'm gonna try to add it to my projects when I have the opportunity.
@DevCastOficial
@DevCastOficial Месяц назад
Thanks a lot for sharing knowledge!
@Byku010
@Byku010 3 года назад
You have done such a great job at explaining things. Really good job dude! Keep it up and greetings from Poland!
@CodeOpinion
@CodeOpinion 3 года назад
Thanks! Glad you like them
@alivateRocket
@alivateRocket 3 года назад
The problem with uncle Bob's definition is that it doesn't describe the cohesion boundary. Cohesion is about what is controlled from within the boundary. If you make the boundary too small, control is outside that boundary, if you make the boundary too large everything fits inside, but it's now the whole system. Discussion about the boundary line is what uncle Bob and many others talk about, that's the process of sizing a module, but that isn't the definition of cohesion. You need to iterate through different boundary options where cohesion is optimised and module size is minimised. So cohesion itself is not the size, it's the criteria for internal control of resources (and dependencies). I will be writing some much clearer articles about this, this year.
@CodeOpinion
@CodeOpinion 3 года назад
Agree about boundaries. A lot of the videos I post are about or revolve around boundaries and defining them. Specifically focusing on behaviors as the primary driver. So in isolation this video and talking about SRP may not seem entirely clear but I hope those that watch more will get a better picture.
@alivateRocket
@alivateRocket 3 года назад
@@CodeOpinion For boundaries I find that "focusing on behaviors" becomes too obscure, and avoids giving the idea of cohesion and coupling a clear definition. I am looking for measurable properties about the boundary itself. For example, breaking down the practical minimal components that a modular system needs - connection to data, control over authorisation, connection to other modules, etc... where are those configured and controlled? Often they are configured and controlled outside of the boundary. I am working on this kind of thinking and I plan to publish quite thorough research in the near future when it's coherent and polished. The factors of "behaviours" are certainly important, but the idea of a "cohesion boundary" needs to be able to stand apart. You certainly bring context to the practice of finding the boundary - context is everything. But then it's good to be able to analyse the outcome objectively with some absolutes. Too many people out there say, "and therefore there's less coupling" - like it's some sort of emotion.
@paragraut3504
@paragraut3504 3 года назад
Amazing and easy explanation
@CodeOpinion
@CodeOpinion 3 года назад
Glad you think so!
@keithnicholas
@keithnicholas 3 года назад
I used to go on a lot about cohesion and coupling, and while they influence my thinking, I feel like they are derivative of things that compose well. Making things that compose well is much more interesting and drives cohesion and (de)coupling. It also influences knowing when cohesion and coupling don't really matter (ie, things that don't have to compose well with other things)
@CodeOpinion
@CodeOpinion 3 года назад
I agree. I'm not always thinking about cohesion and coupling directly. It's more of a by product of how I organize and compose things together. Having said that, understanding or and being aware of cohesion and coupling gives the foundation, which was the intent of the video.
@TimSchraepen
@TimSchraepen 2 года назад
Imho: Coupling and cohesion are heuristics one can use to decide the degree of how composable a thing should be. They’re not mutually exclusive concepts.
@bennet7817
@bennet7817 Год назад
Nice
@Akhbash
@Akhbash 9 месяцев назад
What was the difference between the two implementations showed in this video? Why the last test should've passed?
@francescodente2876
@francescodente2876 3 года назад
I do agree with all you say in the video, but sometimes I tend to group behaviors based on the likelihood of them having to change together, even though they end up not being used together
@CodeOpinion
@CodeOpinion 3 года назад
They may be used apart of the same workflow I assume, not necessarily the same unit of code (class/method/function). Which is still in-line with what I'm saying. The point is that behaviors are the leading driver. Those behaviors are what require data.
@hexdump8590
@hexdump8590 2 года назад
Hi Derek! Again another eye opener video. I like the idea of functional cohesion. It fits and reinforces the idea of the vertical slice architecture where every slice is a high cohesive unit that goes hand to hand with the bounding context concept. But I have a question, the way you split entities by "function" can't lead to duplicate fields in different bounding contexts that will have to be kept in sync? Isn't this something hard to do? I mean if you have the warehouse bounding context and the sales bounding context that both have their vision of the product entity and now someone notice that the sku is wrong and needs to be changed (I just picked the field common in your example, can be any other that is shared) how would you go to solve this issue? It can't be changed in one place, it needs to be changed in every different "Product entity" owned by every bounding context. Please could you comment a bit about it? Thanks!
@CodeOpinion
@CodeOpinion 2 года назад
Data is going to be owned by a service. If you do replicate it to other services, especially identifiers, I'd be surprised those actually change. But if you do have replicated data, just be aware that it could be stale and how you want to manage keeping it up-to date. I'd say most use either event carried state transfer or use events as notifications to then call the publisher. Check out this video: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-qKD2YUTJAXM.html
@AlexMercer00
@AlexMercer00 3 года назад
How does DI work for the delegate and the method implementation?
@CodeOpinion
@CodeOpinion 3 года назад
Register the delegate to a static method that meets the delegate signature. Or use the factory register to resolve a class and pass the method that meets the delegate signature.
@MrAyuub22
@MrAyuub22 3 года назад
Haven't really heard much about delegates in .net core c#. Do you have a video on this?
@CodeOpinion
@CodeOpinion 3 года назад
I don't have anything specific on them I think they are widely underutilized in application code. Func and Action are delegates that are more often in libraries and frameworks.
@andrewthompson9714
@andrewthompson9714 Год назад
@@CodeOpinion I wouldnt know how to write c# without action and func, but especially func. There is value of defining your own delegate types to be more domain specific too
@JRTrue86
@JRTrue86 3 года назад
Thanks for making great videos. I agree with what you're saying about cohesion. However, I don't agree with the delegate solution you proposed in this video. Generally, there's nothing wrong with delegates, they're very useful! By your own definition, in the video, a class should only do one thing. The product service does 4 things. Updates product info, gets by SKU, gets for sale by SKU, and gets all. I don't think that's a bad product service, I think it's fine. BUT, if you wanted to hide the implementation details you would inject an interface for EACH of those functions. IProductInfoUpdater, IProductSkuGetter, etc. (Forgive the shitty names, sorry.) Maybe CQRS? Basically, I don't think you should use delegates for this particular example, in fact I'd say it's a bad idea, but I agree with the principal conclusions and I enjoy your videos. Thanks.
@AlexMercer00
@AlexMercer00 3 года назад
I disagree. If you are gonna break apart the functions away from the ProductService then a delegate for each would fit more than an interface. You use interface to group behaviors, but if your interface only has 1 method each then delegate is perfect choice. Delegates are best seen as Anonymous one-method interface.
@CodeOpinion
@CodeOpinion 3 года назад
Thanks for the comment! I appreciate it. I can see the idea of multiple interfaces, however I'm not sure how an interface with one method is more useful than a delegate. The delegate is more explicit about the dependency in my view.
@JRTrue86
@JRTrue86 3 года назад
@@AlexMercer00 Thanks, always interested in other opinions
@JRTrue86
@JRTrue86 3 года назад
@@CodeOpinion Thanks for the reply. Your point about single function interfaces is fair. I don't think it's a huge deal here. I just don't want new developers littering their code with delegates, which can be difficult to debug (sometimes).
@k3nnydust
@k3nnydust 3 года назад
how do you maintain correct sync between systems in a microservices environment? especially if data is common data is shared between them?
@CodeOpinion
@CodeOpinion 3 года назад
My question would be why do need the data across services? If it's no it to enforce invariants and is solely for view/UI proposes, then that's a different topic 😊
@alivateRocket
@alivateRocket 3 года назад
I'm not a huge fan of ideological microservices, but they are defined as "owning" data. That doesn't have to mean that they need to "own" the data infrastructure and manage the security, in fact, as you elude to, that can cause problems. You can have an ideological microservice architecture if you use a single database system, like PostgreSQL, and use the authorisation tools to limit access to individual microservices. Then if you want to orchestrate data, you can give such a microservice "read-only" access to a wide range of data, so you can JOIN. You can also let your customers loose with something like PowerBI with full read rights across the database.
@m1dway
@m1dway 3 года назад
Should all the products under sales, warehouse and purchasing needs product name as well?
@CodeOpinion
@CodeOpinion 3 года назад
Generally no. Not unless they need it for query purposes, which they could have a local cache copy of if they needed it. I wouldn't think they need it for invariants/business rules.
@chucaraDK
@chucaraDK 3 года назад
I maybe missing the point, but I'm not really sure what the gain is here. You are substituting an interface dependency (IProductService) for a static class dependency (ProductDelegates). I don't see how that changes cohesion. I prefer the inline mocking code in your case, but you lose the ability to use things like .Verify(..., Times.Once) from Moq.
@CodeOpinion
@CodeOpinion 3 года назад
Taking a dependency on an interface where you require one method, and none of the methods relate to workflow, means you're likely going to need to use a limited number of methods on that interface. If that's the case, you don't want the interface, you want a function. The interface isn't a collection a collection of cohesive behaviors but rather a grouping based on data. The benefit of taking the dependency on the function/delegate is because the function is explicit. It defines its inputs and outputs which you can stub. You can stub an interface, but it's not as explicit about what your usage actually is of the interface. Again if the interface has 6 methods and you're usage is only 1, then if your usage changes, so does your test. This is the same with using a delegate but if the delegate dependency changes, you won't even be able to build. If you find value in using mocks and are concerned with if you're code under test is actually calling dependencies (eg using Verify), then keep doing what you're doing. I prefer to use stubs in their smallest form (delegates) instead of mocks.
@chadbennett
@chadbennett 2 года назад
@@CodeOpinion - Does this mean the main driver for choosing delegates over interfaces is testing maintainability/simplicity? I feel like there are more reasons, but I am not sure I understand them. I get the point of an interface being one function and a delegate being a collection of functions, but that alone seems pretty trivial.
@WorthyVII
@WorthyVII 3 года назад
Wait I don't understand, why is the test passing at the end a good thing?
@CodeOpinion
@CodeOpinion 3 года назад
The signature of the dependency was the same. The stub ultimately is the same. If it wasn't it wouldn't build.
@WorthyVII
@WorthyVII 3 года назад
​@@CodeOpinion Ah, because its unrelated right. Ok, I thought I missed something different, thanks!
@TimSchraepen
@TimSchraepen 2 года назад
Because coupling your test code too tightly to production code makes you change your tests everytime you refactor your production code. This sometimes constrains developers from refactoring or makes them spend a lot of unnecessary time when they want to add features. The test still verifies the same intent, but it does so in a way that’s more flexible for future development.
@bernhardkrickl5197
@bernhardkrickl5197 9 месяцев назад
I lol'ed because in your de-coupled products you can only ever *increase* price and cost. You can't decrease it. Commentary about our economic system hidden here ;)
@andrewthompson9714
@andrewthompson9714 Год назад
Never depend on things you don't need. It sends coupling through the roof and confuses the domain responsibilities
@nicholas1460
@nicholas1460 3 года назад
I think you have jumbled together the concept of views with entities and database (de)normalization. None the less, it's a good point that software needs to consider functional and domain aspects.
@ya4eburashka
@ya4eburashka 3 года назад
Well, that code example part seems strange to me. First of all, you have 3 get* methods, but only one is actually used. I'd say, you either don't need that methods in the first place, or you will need them in your handler eventually. Keeping in mind this is made up expample, in real life that handler will (!) need more methods from service after some time. My second thought is that you still need a real implementation for the delegates, which has to be placed somewhere - you've said it in the video, but did not showed it. So the service stays the same, but now we should be searching for the implementation in our project, which slows debugging. Third thought, testing should not affect developing. Those two methods happened to have the same input and output, but in real life you'd need to change that test. After all, changing tests when we change logic is ok, so this approach have no benefits compared to mocking (where we'd need to change mocked method). But mocking have benefits in terms of clarity. With mocking, when I read a test, I can easily see what's happening. I can see, that, if service returned that object, result of this hadler should be that. And I can easily find tests in project for that method. "Delegate version" of this test tells me much less information. If some random method returns some product, it's somehow will be returned from Handle method. What this test actually testing? Delegate invoking? I hope Microsoft done it for me. And your example shows exactly that. Test did not reflect changes in code, so you're testing only a fact that injected delegate got invoked, whatever delegate it may be. I'm not sure that is something I want to test. After all, now the same problem could appear instead in injection logic - we set up a wrong service's method for this delegate and now our tests did not told us about it which defeats whole it's purpose.
@CodeOpinion
@CodeOpinion 3 года назад
Thanks for the comment, let me try address some of the points you made. 1 - The 3 get methods were to illustrate other methods on the interface that are likely used in other places. The point being is that you're injecting a dependency on an interface where you only need 1 of the methods. And this is because that interface is grouped by informational cohesion. 2 - I didn't show the implementation or the interface or the delegate because it wasn't useful the point I was making. You're depending on an abstraction not an implementation. 3 - Yes, in real life, if the dependency on the delegate changed, and it had a different signature, you'd actually wouldn't be able to build. Which means you're getting feedback quicker then running the test and it failing because you failed to mock a method on the interface. You mentioned in the test you can easily see what's being mocked. The same is with the delegate stub implementation. If you're SUT is a black box, and it takes a interface as a dependency, which methods on the interface need to be mocked? You don't know. The delegate is explicit on exactly what the dependency is. It's the exact opposite of "tells me much less information". It tells you more by specifying exactly in the smallest form possible. 4 - Your proving a dependency to your SUT which has an expectation. You're providing that. That doesn't change if your creating a mock or a stub, they are both providing a return value for the SUT to execute. If the SUT stops using that dependency but still accept its, nothing changes. You're not testing that the delegate was called. Hope this answers some of the questions.
@Evgeniy994
@Evgeniy994 2 года назад
Thanks for this video! I like the idea of taking the dependency on the delegate, however, it's not very clear to me how to test the delegate's implementation. Say, I have something like services.AddScoped(x => { ... }); How do I test this? Thanks.
@CodeOpinion
@CodeOpinion 2 года назад
Provide an implementation of the delegate for test, as shown in the later part of the video. If you're using the service provider apart of your test, then you'd have to register (add) the implementation.
Далее
STOP dogmatic Domain Driven Design
7:18
Просмотров 132 тыс.
Is a REST API with CQRS Possible?
16:46
Просмотров 35 тыс.
Беда приходит внезапно 😂
00:25
Просмотров 891 тыс.
ХЕРЕЙД БОИТСЯ МОЕЙ СОБАКИ!
37:08
SOLID Principles? Nope, just Coupling and Cohesion
13:56
Coupling and Cohesion Explained
11:43
Просмотров 3,3 тыс.
Keep your project structure simple!
15:08
Просмотров 16 тыс.
Aggregate Design: Using Invariants as a Guide
8:35
Просмотров 37 тыс.
AVOID Entity Services by Focusing on Capabilities
7:31
Understanding and improving coupling and cohesion
59:17
Loose & Tight Coupling: Why Code is Hard to Change
17:40
Managing complexity | Software fundamentals
6:17
Просмотров 1,7 тыс.
Vertical Slice Architecture, not Layers!
46:24
Просмотров 115 тыс.