Highly COHESIVE Software Design to tame Complexity

  Рет қаралды 13,613

CodeOpinion

CodeOpinion

Күн бұрын

Пікірлер: 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 2 жыл бұрын
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
@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.
@hpdipto
@hpdipto 3 жыл бұрын
"When I'm thinking about cohesion, I'm thinking about functional cohesion. Not informational cohesion." -- precisely clear!
@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 3 жыл бұрын
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.
@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.
@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 3 жыл бұрын
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 3 жыл бұрын
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: kzbin.info/www/bejne/p3ynY4yLia90jq8
@Akhbash
@Akhbash Жыл бұрын
What was the difference between the two implementations showed in this video? Why the last test should've passed?
@DevCastOficial
@DevCastOficial 5 ай бұрын
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
@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).
@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 2 жыл бұрын
@@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
@paragraut3504
@paragraut3504 3 жыл бұрын
Amazing and easy explanation
@CodeOpinion
@CodeOpinion 3 жыл бұрын
Glad you think so!
@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.
@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.
@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.
@bernhardkrickl5197
@bernhardkrickl5197 Жыл бұрын
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 ;)
@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 3 жыл бұрын
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.
@bennet7817
@bennet7817 Жыл бұрын
Nice
@andrewthompson9714
@andrewthompson9714 2 жыл бұрын
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
CodeOpinion
Рет қаралды 134 М.
SOLID Principles? Nope, just Coupling and Cohesion
13:56
CodeOpinion
Рет қаралды 33 М.
Wait for the last one 🤣🤣 #shorts #minecraft
00:28
Cosmo Guy
Рет қаралды 17 МЛН
How it feels when u walk through first class
00:52
Adam W
Рет қаралды 26 МЛН
CAN YOU DO THIS ?
00:23
STORROR
Рет қаралды 42 МЛН
Aggregate Design: Using Invariants as a Guide
8:35
CodeOpinion
Рет қаралды 38 М.
Balancing Coupling in Software Design - Vlad Khononov - DDD Europe 2023
50:43
Domain-Driven Design Europe
Рет қаралды 10 М.
Aggregate (Root) Design: Separate Behavior & Data for Persistence
10:47
Cohesion and Coupling: Write BETTER PYTHON CODE Part 1
26:14
ArjanCodes
Рет қаралды 217 М.
START with a Monolith, NOT Microservices
8:07
CodeOpinion
Рет қаралды 19 М.
Understanding and improving coupling and cohesion
59:17
Feabhas Ltd
Рет қаралды 3,5 М.
40 Years Of Software Engineering Experience In 19 Minutes
19:10
Continuous Delivery
Рет қаралды 92 М.
Coupling and Cohesion Explained
11:43
Gui Ferreira
Рет қаралды 6 М.
Wait for the last one 🤣🤣 #shorts #minecraft
00:28
Cosmo Guy
Рет қаралды 17 МЛН