I am looking into event sourcing and I have a few thoughts that I need to get my head round.
Take for example an online shop -
The customer adds an item to the basket and saves their order.
The command handler could create an order on the customer aggregate root and add an OrderCreated event which contained the customer id, order id, item id, quantity and unit price. All pretty straight forward but what if the aggregate needed to check to see if that item was on special offer?
If this was for example a basket service would it subscribe to events from the catalog service and store it's own projections of the catalog service which it could then use, so then the basket service would comprise an event store and also some form of projection of the catalog service?
Or if in the example I've just described, if the basket and catalog functionality were part of the same application and they only held event data, then when a customer creates an order the handler would pull all ordered items from the event store via a repository, apply all events to them and then return them to the handler in order to check if the item was on special offer.
what if the aggregate needed to check to see if that item was on special offer?
It needs to execute a query to get the information it needs.
From the aggregate point of view those data are external, so it (or the handler sending the command to it) needs a query to access that information.
How the query work is up to you (there are pros and cons for each way), the query handler can:
Call a repository that loads an aggregate and check for the special offer (you can also think to have event sourcing in just one part of your system and having this part using another way to store dara, or not)
Make a remote call to the Catalog Service API to check for the special offer
Do a query to a local db, that is populated reading events emitted by the catalog service and stored "locally" to the basket service
Related
I am building the inventory service, all tables keep track the owner of each record in column createdBy which store the user id.
The problem is this service does not hold the user info, so it cannot map the id to username which is required for FE to display data.
Calling user service to map the username and userid for each request does not make sense in term of decouple and performance. Because 1 request can ask for maximum 100 records. If I store the username instead of ID, there will be problem when user change their username.
Is there any better way or pattern to solve this problem?
I'd extend the info with the data needed with from the user service.
User name is a slow changing dimension so for most of the time the data is correct (i.e. "safe to cache")
Now we get to what to do when user info changes - this is, of course, a business decision. In some places it makes sense to keep the original info (for example what happens when the user is deleted - do we still want to keep the original user name (and whatever other info) that created the item). If this is not the case, you can use several strategies - you can have a daily (or whatever period) job to go and refresh the users info from the user service for all users used in the inventory, you can publish a daily summary of changes from the user service and have the inventory subscribe to that, you can publish changes as they happen and subscribe to that etc. - depending on the requirement for freshness. The technology to use depends on the strategy..
In my option what you have done so far is correct. Inventory related data should be Inventory Services' responsibility just like user related data should be User Services'.
It is FE's responsibility to fetch the relevant user details from User Service that are required to populate the UI (Remember, call backend for each user is not acceptable at all. Bulk search is more suitable).
What you can do is when you fetch inventory data from Inventory Service, you can publish a message to User Service to notify that "inventory related data was fetched for these users. So there is a possibility to fetch user related data for these users. Therefore you better cache them."
PS - I'm not an expert in microservices architecture. Please add any counter arguments if you have any.*
I work on eCommerce app. I use micro-services with ddd. I have product service. It store title description etc. Pricing service store product prices and promotions. Category service store categories and product category. Shipment service store all about shipment data and product shipment details dweight cargo firm isFreeCargo etc.
I dont Want to use composition pattern for searching. I create search service which store all datas of services. Pricing, product information, shipment information, category information etc.
I concat all data with events. Like cQRS
So all services communicate each other with events.
my problem is;
when CreateProductRequest come To product service . It includes categoryId, price, shipmentFirm etc. I mean not only product service information.
I only save title description etc in product service. There is no data like Price or category in product service. because They dont belong this domain. When product created and event raise productCreatedEvent that includes created product id and other service informations come with request.price , category etc.
Other services listen this event. And consume. Pricing save productId and price. Cagegory save productId and categoryId ...
And search service consume event,too. But there is no data about price or category because they just published.
Okey I can prove consistency with events when price change or category After saving. but at first maybe there is not going to be price or category for 2-3 sec.
how canI save category and price for search service immediately. because there is no price and category denormalize and caculated data in productCreatedEvent .There is just reference Ids like categoryId or not caculated price.They are going to be exist on search service after own services save and publish events like ProductCategoryUpdated or ProductPriceCaculeted
.
First of all. What I can see is that price and category services are very related to Product and they dont have strong functionality on its own so I suggest you to merge the three of them in one service. If the price service is used to create promotions, I would make a promotion service wich communicates with product service asking for the price of a product or a category of products, and apply them the corresponding discount.
The first problem has to do with what I have just said, because price, category and product are so related you have to pass all the data when you create a product, but because they are different services, product service should not be aware of this information. In case you want to do this way, you should create prices and categories independently of the product and when these are created each service should send a priceCreated or categoryCreated event that will be consume by anyone interested in. For example the search service.
For the second question, the best approach I think is the first one, first because It wont couple services. The cart service only have the ids no other information. And when you want to query another information you only have to make the query to the proper service as usual, so no coupling is created.
And second, the main drawback of the second option is that you are including logic related to cart service in all the other service. For each one you have to listen events and instead of make logic related to the service like update some info, they are sending info to another service. It is like a hidden command message
I have entity "Work Order" for which I have defined many custom views. Work Orders can have records with statuses as "active ,cancelled, closed, inprogress, submitted" etc. My requirement is - currently logged in user who belongs to a specific team "sales representative" should be able to see all records on view.This can be done easily, but If current logged in user does not belongs to "sales representative" team, she should not be able to see "cancelled" records on view but all other record should be visible to her. How can I achieve this using custom filters if it is possible? Or by code changes?
It is possible to do this with custom code. Without questioning the "why" you'd like to do this (possibly it's sensitive information or something?), you can achieve it using a RetrieveMultiple plugin registered on the pre-operation event. Within this plugin one of the input parameters passed in is called "Query" and will have a QueryExpression. You can simply add a filter to this query in the plugin and the relevant rows will be filtered out. Something like this:
var query = (QueryExpression)context.InputParameters["Query"];
var condition= new ConditionExpression()
{
AttributeName = "statuscode",
Operator = ConditionOperator.NotIn,
Values = { 2, 3 } // Or whatever codes you want to filter!
};
query.Criteria.AddCondition(condition);
To check the current user you can grab the user id from the plugin context and retrieve the necessary info you would like to check.
Doesn't sound like this is possible with advanced find alright. You may be able to achieve it using security roles though. If you could assign cancelled work orders to a specific team, and then organise your security setup so that users who are not sales representatives can't see work orders from that specific team, then it might work. Unfortunately you would have to reassign the cancelled work orders which is not always an option.
Otherwise, you might have to go with a separate view for cancelled work orders, out of the box advanced find should allow you present a blank grid of you are not on the right team. But now obviously you are not presenting a whole view of the work orders.
In general I would go with the security option, and just make it work. Any other option is just a stop-gap. Users can always create custom views, so if you don't lock down access using security roles, the data is still accessible in indirect ways.
Two questions
1) How to model aggregate and reference between them
2) How to organise/store events so that they can be retrieved efficiently
Take this typical use case as example, we have Order and LineItem (they are an aggregate, Order is the aggregate root), and Product aggregate.
As LineItem needs to know which Product, so there are two options 1) LineItem has direct reference to Product aggregate (which seems not a best practice, as it violate the idea of aggregate being a consistence boundary because we can update Product aggregate directly from Order aggregate) 2) then LineItem only has ProductId.
It looks like 2nd option is the way to go...What do you think here?
However, another problem arises which is about building a Order read/view model. In this Order view model, it needs to know which Products are in Order (i.e. ProductId, Type, etc.). The typical use case is reporting, and CommandHandler also can use this Product object to perform logic such as whether there are too many particular products, etc. In order to do it, given the fact that those data are in two separate aggregate, then we need 1+ database roundtrips. As we are using events to build model, so the pseudo code looks like below
1) for a given order id (guid, order aggregate id), we load all the events for it; -- 1st database access
2) then build a Order aggregate, then we know which ProductId are referenced in Order;
3) for the list of ProductIds, we load all events for it; -- 2nd database access
If we build a really big graph of objects (a lot of different aggregates), then this may end up with a few more database access (each of which is slow)...What's your idea in here?
Thanks
Take this typical use case as example, we have Order and LineItem (they are an aggregate, Order is the aggregate root), and Product aggregate.
The Order aggregate makes sense the way you have described it. "Product aggregate" is more suspicious; do you ask the model if the product is allowed to change, or are you telling the model that the product has changed?
If Product can change without first consulting with the order, then the LineItem must not include the product. A reference to the product (aka the ProductId) is ok.
If we build a really big graph of objects (a lot of different aggregates), then this may end up with a few more database access (each of which is slow)...What's your idea in here?
For reads, reports, and the like -- where you aren't going to be adding new events to the history -- one possible answer is to do the slow work in advance. An asynchronous process listens for writes in the event store, and then publishes those events to a bus. Subscribers build new versions of the reports when new events are observed, and cache the results. (search keyword: cqrs)
When a client asks for a report, you give them one out of the cache. All the work is done, so it's very quick.
For command handlers, the answer is more complicated. Business rules are supposed to be in the domain model, so having the command handler try to validate the command (as opposed to the domain model) is a bit broken.
The command handler can load the products to see what the state might look like, and pass that information to the aggregate with the command data, but it's not clear that's a good idea -- if the client is going to send a command to be run, and you need to flesh out the Order command with Product data, why not instead have the command add the product data to the command directly, and skip the middle man.
CommandHandler also can use this Product object to perform logic such as whether there are too many particular products, etc.
This example is a bit vague, but taking a guess: you are thinking about cases where you prevent an order from being placed if the available inventory is insufficient to fulfill the order.
For real world inventory - a physical book in a warehouse - that's probably the wrong approach to take. First, the model itself is wrong; if you want to know how much product is in the warehouse, you should be querying the warehouse, not the product. Second, a physical warehouse is not constrained by your model -- calling the addProduct method on a warehouse aggregate doesn't cause the product to magically appear there.
Third, it probably doesn't match very well with what your domain experts want anyway. If the model says that the warehouse doesn't have enough product, do you think the stake holders want the system to
tell the shopper to buy the product somewhere else, or...
accept the order, and contact the supplier for a new delivery.
Hint: when in doubt, carefully review how amazon.com does it.
I am writing a magento module for a gateway that authorizes loans as a payment system.
Since there is a relatively high probability the customer will get declined, I chose to implement this using getCheckoutRedirectURL() (placing the loan steps before 'place order' in the checkout flow) so that in the failure case, I can easily return the customer to the payment choice page.
I then do the gateway API call in my redirectAction in my controller.
As a result, I get a URL to open in a lightbox to take the customer through the loan process, as well as some id's from the loan gateway.
I would like to store these additional id's as part of the quote and later copy them to the order when I convert the quote to an order (I did the conversion similar to google checkout - based on a callback from the loan gateway).
However, I cannot figure out how to persist data on the quote.
The obvious way:
$quote->setCustId($custId);
$quote->save();
doesn't work; the additional data does not get stored in the database and hence is not available in the postback handler to convert quote to order.
The same happens for
$quote->setData('custId', $custId);
$quote->save();
(and I assume this is just a more explicit form of the first in the sense that it doesn't use the magic setter/getter)
I've seen references to setAdditionalData (for example, here) but that looks to be available only on payment objects, which I don't think I have yet on the quote (although I could be wrong ?)
Is there any way to store some fields on an order in the database without having to actually add database fields for them ?
You will need to add actual columns to sales_flat_quote table to store extra variables in quote object.
Since the stuff you are making is related to payment method instead I'd suggest you to store those variables in additional_information dataset that is stored to sales_flat_quote_payment table as serialised php array and payment methods can set data to this variable as follows:
$quote->getPayment()->getInfoInstance()->setAdditionalInformation($key, $value);
$order->getPayment()->getInfoInstance()->setAdditionalInformation($key, $value);
see more from app/code/core/Mage/Payment/Model/Info.php
the only downside for this is that it is stored as serialised array at the end which means no direct sql access later if you somehow need to depend on this data, offer filters and such.