Are you using UI and/or ViewModel Composition? How do you tackle composing data from multiple services?
@andrewcalderwood6102 Жыл бұрын
We use Graphql With a gateway aggregation service. means the client just treats it like a single backend.
@renedekkers735 Жыл бұрын
I prefer the idea of a read model in a separate database with tables that contains data that is optimized for a specific view and is kept up to date with integration events. This will keep the queries very simple and fast
@anthonylevine8783 Жыл бұрын
@@andrewcalderwood6102 Been looking at this option for awhile. Have you run into any issues? And what stack are you using for the backend if you don't mind me asking?
@dino56ac49 Жыл бұрын
I've added a NoSQL storage to my BFF that stores projections tailored for the BFF access patterns. It's integrated to the services through events. Thanks for the video! I've been looking at this problem and how it forces you to shape your APIs in a way that I don't find cohesive with my domain concepts
@Wouldntyouliketoknow2 Жыл бұрын
How does gateway aggregation solve the issue of fetching a page of data with sorting and filtering accross the boundaries? I guess it has to pull back all data from all sources and do the join in memory which could be a massive waste of resources and make this approach just not viable for these particular circumstances? In the interest of being consistent and because you know this aggregation approach can't cater for all scenarios, it's better to solve the issue of how to materialise a view that is ready to be read from (I.e basically a cache as explained in this video) and that means something like event carried state transfer to do that cleanly. What I am saying is that once you have distributed data scenarios, there is a type of a requirement that can force your hand into this option and therefore its better to plan for how you will solve it when that requirement arises, and this begs the question as to whether you should do gateway aggregation at all because it can muddy the water - i.e its easier to reason about the system if you know that all views accross boundaries are backed by the same mechanism and subsystem. If some areas use gateway aggregation and some use cache there is perhaps always an extra step to remember which functionality maps to which mechanism. It's a tradeoff because gateway aggregation is technically a "live" view so in that sense is more what users and product owners expect often when putting forth user stories. However once you venture into distributed systems I feel certainly product owners should be comfortable with the idea of caches driving views of data and the concepts of eventual consistency.
@leoMC4384 Жыл бұрын
I watch your videos every once in a while. I don't understand most things, I'm an old self-taught developer looking for his first job, I'm 37. Your channel keeps me engaged, I wish I could understand everything some day. Keep it up. 👍
@CodeOpinion Жыл бұрын
Keep at it! Thanks for watching
@iliyan-kulishev Жыл бұрын
Welcome to the club, buddy. 33 here, just started my first dev job. Learning so much from this channel. I also don't understand all things, but the general concepts and ideas become clear. I can discuss microservices fx with somebody that has years in that without actually having worked on that.
@fr3fou Жыл бұрын
Wouldn't storing catalog-related data in the Sales service violate SRP? I kind of find it counter intuitive for the BFF to query the Sales service for rendering a catalog page to be honest. Imagine you also have to render the product stock, something which you'd most likely store in the Inventory service, would you also propagate changes from there to the Sales service? I think that this can get really complicated when you want to query things like "give me all paginated products, sorted by price, with brand X which are in stock" - not sure how you're supposed to compose such query
@CodeOpinion Жыл бұрын
Yes, it can get complicated, which is why this becomes an issue. Most often this composition for this type of listing that involves fitlering/sorting/pagination gets very limited. Often this can involve a service that is specifically for aggregating all this data into a central spot which owns that capability. You'll also notice that it becomes very limited in the sense you can just filter on everything. Go check out something like Amazon, you can't filter on everything under the sun. You can't change the pagination size. You're limited to the sorting options.
@johnporter8896 Жыл бұрын
If you have a frontend service that needs data from multiple other services then the best approach is a separate read model as a service that has already aggregated all the relevant data together. That way you have a totally denormalised view model that is queryable separately to the other services!
@alexweisberger1669 Жыл бұрын
That’s what he described here. The event carried state transfer is what keeps a read model in another service in sync with the data from the producer.
@kamilbeben9900 Жыл бұрын
@@alexweisberger1669 the difference is, the video shows that the sales service did that. What John described on the other hand, is completely another service, let's call it Product pagination service which aggregates all the data that you need to execute these filter / sorting, and is doing so by listening to events dispatched by product, sales and stock services.
@egorsozonov7425 Жыл бұрын
So apparently the solution to microservice composition is... to make a monolith? Because that's what the third solution is. If Sales stores all the Catalog data and you don't need to consult Catalog to get its data, then you have a monolith again. And what if you had N microservices with potentially 2^N different kinds of requests, how would you handle the cache then? I think the BFF solution is the best, and it can handle the "sort by price" scenario as follows: 1) SELECT ProductId, Price FROM SALES ORDER BY Price ASC SKIP Pages*PageSize LIMIT PageSize 2) SELECT * FROM Catalog WHERE ProductId IN (...result of (1)...). There's just one request to each microservice, no N+1 queries, and we only get the data for one page at a time. And no monolith.
@CodeOpinion Жыл бұрын
As mentioned with event-carried state transfer, there are some serious pitfalls and I'd rather use events as notification for workflows and not data propagation. I've covered this in other videos. However, there really is no different then building out a reporting/BI solution where you would need all this relevant data aggregated. So as long as you are NOT using it for commands and business logic and expect to be using consistent data. The trouble with event-carried state transfer is people using it as a crutch because they think they need data within a particular boundary, when they really don't. Typically if they weren't using events, they'd be doing RPC calls, which is equally as coupling, but more so because it's temporal coupling as well.
@arielmoraes9427 Жыл бұрын
Excelent video, in the past few weeks I've been working my mind around such problems and my conclusion is: business wanting views that provide the ability to sort, page and filter in a very dynamic way, are more aligned to some sort of analytical OLTP, so why not build an analytical service that could provide all the needed information and be built to provide that data in the best way possible? Furthermore that could be used to provide external APIs using GraphQL or OData and also be monetized depending on the business, thus providing easy analytical capabilities too.
@andreipacurariu2013 Жыл бұрын
Great video Derek, and a good summary of the composition patterns. On the topic of client-side composition, I would like to share a different technical approach which avoids the N+1 query pattern you correctly highlight. For this, it's useful to differentiate between "dumb" components which deal only with presentation and "smart" components which interact with backend APIs. In my experience, the best solution is to have the pages in the application (e.g. the page were you're displaying the grid and filters in your example) act as containers for "dumb" components such that only the pages are "smart" components. Thus, the pages implement the actual composition and fetch all the data while the "dumb" components (e.g. product image, product price, etc.) that make up the page are simply passed in the data and display it. In your example, you could have two different client-side services one for the Product Catalog Service and one for the Pricing Service and the page can invoke both of these in a sequence - first get the products that match the filter from the Product Catalog and then retrieve the appropriate prices for these from the Pricing Service passing in an array of product SKUs (a batch query). Thus, with only two sequential requests you can get all the necessary data for the page avoiding the select N + 1 problem. It's noteworthy also that if you need to get product data from other services (not just prices) these other requests could be performed in parallel with the pricing request since they're all based on the same list of SKUs. This is a client-side composition pattern that I've used with good results.
@stefan-d.grigorescu Жыл бұрын
I think the issue about this is what Derek presented at the "List, pagination, sort, filter" section. In your exemple you made all these based on information contained within the first service, then just completed your products' info with the data queried in batch. However, fetching a page of 100 cheapest products that are also filtered by some data in the other service is still a problem. Note that loading all the products data from both services in the page components then doing all the filtering and sorting on FE side might get difficult as the total count of products increases
@barrydev Жыл бұрын
Excellent video! Love the content and concise format. One aspect of service-based architecture that's sometimes not given a lot of thought is the implicit architecture characteristic coupling between services when they're participating in a single request. i.e. when a user makes a request for the product listing view, it is a requirement that both Sales and Catalog are currently available. That guarantee, if not explicitly catered for via some coordinated infra and monitoring, can completely ruin the end user experience. A BFF with its own local cache would best suit a scenario where uptime cannot be guaranteed for all participating services - with the trade-off of stale data, as mentioned.
@AndreasRavnestad Жыл бұрын
6:49 - 7:50 Strong evidence that you are in the progress of building a distributed monolith. Don't do this.
@CodeOpinion Жыл бұрын
As I've replied in a bunch of comments, I'd prefer not to do data propagation via event-carried state transfer as well as it has a lot of implications. However, there are situations where I think it's entirely valid, such as query only purposes such s reporting. Ultimately a report is a query.
@CarlosAlbertoBrasil Жыл бұрын
Thank's you solved one of my doubts about microservices
@brandonlange2260 Жыл бұрын
Really interesting take on an underrated problem. I think a good addition to add to this is a possible requirement whereby you need to display current stock, therefore needing to include some kind of warehouse boundary to this all. Because stock obviously fluctuates far more frequently than price, causing what I think becomes a pretty interesting topic. (not to even mention specials) 😅
@CodeOpinion Жыл бұрын
Yes, or a product rating/review that is likely in another service.
@FISS007 Жыл бұрын
For the sorting problem, why not do it the other way around ? The sales boundary stores prices and products ids, you query products ids and prices from the sales boundary, and then you query products infos from the catalog by the given IDs. In general, you query the ids from the boundary that owns the sorting/paging critera and then query the rest from the corresponding boundaries given the returned ids (since items ids are stored in every boundary).
@CodeOpinion Жыл бұрын
Add another service into the mix. Say the product rating. Now you want to filter by product name (catalog), filter and sort by rating (review), and sort by price (sales).
@adambickford8720 Жыл бұрын
If stale data is a concern, version it. i.e. optimistic locking. Not a silver bullet but caches are pretty important for anything involving a user experience.
@UdiDahan-jp4xy Жыл бұрын
Another form of composition is useful for advanced search scenarios: Engines. For more info on this approach, see my presentation on the topic: kzbin.info/www/bejne/fKbElJKVnJ5lbqc
@xoca7171 Жыл бұрын
How does the "transparent" approach solves the sorting problem?
@edwnmrtnz Жыл бұрын
interested on this too.
@CodeOpinion Жыл бұрын
It doesn't. It's just an approach to use, assuming you don't have that issue to have ViewModel composition without having to write manual BFF code to do all the appropriate calls to each service.
@sorteslyngel2k Жыл бұрын
This is a good display of why microservices architecture should be avoided at all costs. There simply is no good solution to this problem... and it has a high effect on the end user of the system. This is why so many implementations end up having service-to-service communication.
@CodeOpinion Жыл бұрын
I think defining boundaries is incredibly important however the confusion lies in assuming that logical boundaries are physical boundaries. I don't think you should avoid defining logical boundaries. I think you should only define physical boundaries as logical ones if you actually need to.
@ramanam123 Жыл бұрын
This is definitely helpful but what about the scenario where we already have data and want to use this approach going forward What are the strategies to kind of catch-up first and then have this approach being followed going forward
@allinvanguard Жыл бұрын
Genuine question, would you say there is also a "threshold" at which gravitating back towards on-demand synchronous RPC calls is the better fit? With data redundancy across services, it also sounds like it could spiral out of control fairly quickly since keeping data in multiple places also requires keeping it up to date, to some extent at least. I assume that this can also become messy with scale.
@CodeOpinion Жыл бұрын
The listing/grid example is usually the most typical because it involves so many different services and you generally want filtering/sorting/paging. Instead of having all kinds of services propagating data all over the place (nightmare) you'll generally end up with a service that's entire purpose is for searching. But its still limited in functionality. As an example, go look at Amazon. You can't filter/sort/page by whatever you want.
@allinvanguard Жыл бұрын
@@CodeOpinion Ah, I see, I didn't even get that far into the video yet. Thanks!
@user-bi4wj3fh8y Жыл бұрын
7:56 so dose that mean I don't need a BFF anymore?
@nikolayivanov9984 Жыл бұрын
Great one! I have the following scenario. Let's say we want to create a Product in service A, upon creation an event is emitted and sent to service B, where the logic decides what label it should put on the product. The logic for the label is in service B since that service handles a lot of other products and puts labels, that do not come from Service A, but from an external service. From a UI perspective, once we create a product, we want to view it immediately with the label. The current problem is that the message might not have been consumed fast enough and the data is missing from Service B which we use for reading. Are there patterns that handle these scenarios?
@CodeOpinion Жыл бұрын
So eventual consistency is the issue. Check out kzbin.info/www/bejne/rXa4hYCrh7iNipI
@Galakyllz Жыл бұрын
Another great video. Thanks.
@thedacian123 Жыл бұрын
Does not ,gateway and bff patterns make the same stuff i mean model aggregation ,reverse proxy ,security etc?
@takyuchan7920 Жыл бұрын
Nice video
@CodeOpinion Жыл бұрын
Check out this video, might add more context: kzbin.info/www/bejne/l5-vXoV9gN-Sd80
@RC-cy7pd Жыл бұрын
Where would GraphQL Federation fit in?
@ArinSinabian Жыл бұрын
If we add a new service that combines data from both catalog service and from sales services via event carried state transfer I think is a better approach. I am thinking that we don't need "pollute" sales service with data from catalog service. I rather let sales be sales and have a new service called "product query service" that combines data from catalog service and sales services. I think there is a risk we add more fields to products from another service say "rating service". Maybe we want to be able to sort by rating then we need to pollute sales service with that data as well. In my opinion it would be better with a separate service and it would also have single responsibility to just query/page/sort products. What would be the downside of going doing this? Maybe both approaches have pros and cons?
@CodeOpinion Жыл бұрын
Yes, moving data to an aggregated service is a similar approach you'd use specific to reporting. You need to compose all the data together to report on. Purely for query purposes.
@BertrandLeRoy Жыл бұрын
Hi Derek. Excellent video as usual. The topic of view model composition really resonated with me because of my involvement with Orchard, which is largely built around this idea. Did you ever get a chance to check it out and if so, what are your thoughts?
@CodeOpinion Жыл бұрын
I'm familiar of Orchard but haven't really dug too deep in it. Any references on where to start?
@David-rz4vc Жыл бұрын
What about having just one database? Each microservice has separate schema; some boundaries there. Then the BFF does the query across the scheme?
@CodeOpinion Жыл бұрын
Well you'd be integrating at the database. Not personally a fan as it's hard to change and evolve (version) as a change to the underlying schema involves the boundary owner of the data and the BFF. Versioning
@MrDomenic123 Жыл бұрын
Thanks a lot for this content, Derek! Do you know if there is an "equivalent" service bus for Java?
@CodeOpinion Жыл бұрын
I'm not in the JVM/Java ecosystem, so I can't really recommend anything. From what I'm told, take a look at eventuate.io
@LarsKemmann Жыл бұрын
I agree with @fr3fou, the "fat events" approach violates SRP which in turn makes the system harder to maintain. Wouldn't a better approach be to add a sort/page/filter endpoint on the Sales service, something like "Give me the next X product IDs when sorting by price", and then passing those IDs to the Catalog service via an endpoint that accepts a set of product IDs and returns their catalog information?
@CodeOpinion Жыл бұрын
Yes, that's possible. The immediate question I would get suggesting that from someone would be then, well how do you also filter on the brand? That belongs to the catalog service. Now were starting at the catalog service, filtering there, going to the sales service and based on those ProductIDs sorting by the price. But you also want to filter on some other on what is free shipping, which belongs to another service. Now what? My point being is there isn't a right/wrong answer, it depends on your requirements. There are tradeoffs to everything. As mentioned in other comments, there are different solutions to this involve having a singular aggregated service that handles this instead of propagating data all over the place, which is a nightmare.
@LarsKemmann Жыл бұрын
@@CodeOpinion Yep, that makes sense. (KZbin hadn't shown me your response to that comment yet when I posted this... speaking of stale data. 😂)
@anthonylevine8783 Жыл бұрын
Another point to add is that would you consider having a reporting database to violate the SRP rule? Of course not. This is the same concept. SRP has nothing to say about where/how you store your data, it's about what responsibilities you have (with that data).
@LarsKemmann Жыл бұрын
@@anthonylevine8783 You're right! I overstated that. It would have been better to say that, as a consequence of SRP, only one service should ever *own* a piece of data -- but there's nothing *inherently* wrong with sharing that data with other services *as long as* those services are simply consuming that data (and, if the services are distributed, assuming that we either have distributed transactions or we're okay with eventual consistency).
@PiesekLeszek90 Жыл бұрын
So instead of having one database, we create multiple specialized databases.. that also save other, potentially stale data just for queries? This sounds like we'll end up with multiple copies of one big database but having to deal with stale data on top of that. Also, why limit ourselves in the order of requests or sorting only when fetching from a service? If we want to sort by price, query sales service with ordering first, then load catalog query into the prices. They both need to mach by sku anyways! But ok, you'll probably say "but pagination can be done only by catalog service" or that you want to sort by name or something, that is possible by simply merging the data differently, fetch the paginated products then fetch ordered prices using list of sku's, then merge products into prices, keeping the price ordering, falling back to product order if needed, done. Limits can be done same way, simply dropping already fetched records that don't match sku from other list. It's all a matter of ordering your requests/data merges and it's simpler than it sounds. I also want to mention, with modern SPA you can do what BFF would in some cases, having a wrapper component "product list" that does fetching of price and product then passing them down to child components is absolutely ok. Fetching prices one by one in a list just sounds like a bad code. I'm not saying BFF's are bad, it's just this example. BFF is great if you have a RESTful API or multiple apps that use the same services.
@CodeOpinion Жыл бұрын
I'm not suggesting event-carried state transfer to propagate data as a always solution, as I mentioned in the video as it has a lot of pitfalls. I mentioned it because it is popular and does have a use-case when the data your keeping for a local cache is non-volatile. There are situations it can be helpful. As for the sorting/paging/filtering. You want to sort by price and review rating. All three are from 3 different services. This is often the case for using event-carried state transfer and having a main aggregate that contains all the primary data you need for all this. Most often however it hast o be limited to some degree as you aren't going to support filtering/sorting on everything imaginable.
@anthonylevine8783 Жыл бұрын
"It's all a matter of ordering your requests/data merges and it's simpler than it sounds." isn't even remotely true. Let's say Service A contains a list of people (and their names) and Service B contains a list of all of the products they've bought (not every registered user has bought something btw). What if I want the top 10 most expensive products bought by people named "Bob"? How would you do that in your example without possibly needing to load every user record with the name of Bob?
@Vangerdahast Жыл бұрын
Can't BFF gather all that information. So for all queries it'll use the data it stores, for commands it'll pas the request to the respective service?
@CodeOpinion Жыл бұрын
Yup
@LotnyLotnik Жыл бұрын
I don't think this will be a problem in Microservices service. Just add another microservice that will handle caching of both image, name and price. Then you UI will hit just one microservice! Easy
@CodeOpinion Жыл бұрын
Yes, that's an option to have an aggregated service. "Easy", I wouldn't use.
@shahrazkl Жыл бұрын
Amazing!
@bobbycrosby9765 Жыл бұрын
As far as fat events go... In the web world data is always stale, it's just the time horizon you're talking about. Even in a monolithic SSR webapp, the html you're sending across with a price is client side state that can become stale from the source of truth - the server. You already should be thinking about this stuff and deciding if its something you need to deal with or not, but sadly many people don't seem to.
@CodeOpinion Жыл бұрын
Agree. The moment you fetch data from a backing store, it's stale. Making business (logic) decisions based on that data however is the issue, as mentioned. Data consistency becomes a problem in that case.
@nove1398 Жыл бұрын
It is really not covered enough
@florianfanderl6674 Жыл бұрын
What do you think about CQRS? Building a separate application for this list page, that listens to changes from the product catalog and sales service and combines them into a new view in a separate database. I personally find UI composition the last thing I would do.
@florianfanderl6674 Жыл бұрын
Ah shit 😁 the answer is in the end of the video 😁👍I was too fast
@Kubkochan Жыл бұрын
I don't think this is CQRS. CQRS IMHO should not be cross boundaries. This is more like creating report view from ecst.
@florianfanderl6674 Жыл бұрын
@@Kubkochan my definition of CQRS is, that there are different projections (views) of data based on a domain model. Usually either based on the events that you send cross the domain boundary or the events from eventsourcing. But I'm not sure if my definition is correct 😉