Monday, May 10, 2021

Health Risks Linked to Obesity


 Obesity is a term that means you have a body mass index (BMI) of 30 or higher. It makes you more likely to have conditions including:

Heart Disease and Stroke

Extra weight makes you more likely to have high blood pressure and high cholesterol. Both of those conditions make heart disease or stroke more likely.

The good news is that losing a small amount of weight can reduce your chances of developing heart disease or a stroke. Losing even more weight has been shown to lower the risk even more.

Type 2 Diabetes

Most people who have type 2 diabetes are overweight or obese. You can cut your risk of developing type 2 diabetes by losing weight, eating a balanced diet, getting adequate sleep, and exercising more.

If you have type 2 diabetes, losing weight and becoming more physically active can help control your blood sugar levels. Becoming more active may also reduce your need for diabetes medication.

Cancer

Cancers of the colon, breast (after menopause), endometrium (the lining of the uterus), kidney, and esophagus are linked to obesity. Some studies have also reported links between obesity and cancers of the gallbladder, ovaries, and pancreas.

Gallbladder Disease

Gallbladder disease and gallstones are more common if you are overweight.

Ironically, weight loss itself, particularly rapid weight loss or loss of a large amount of weight, can make you more likely to get gallstones. Losing weight at a rate of about 1 pound a week is less likely to cause gallstones.

Not everyone who is obese has these problems. The risk rises if you have a family history of one of those conditions.

Also, where your weight is may matter. If it's mostly around your stomach (the "apple" shape), that may be riskier than if you have a "pear" shape, meaning that your extra weight is mostly around your hips and buttocks.

Here's a closer look at seven conditions that are linked to being obese or overweight.

Sleep apnea can cause a person to snore heavily and to briefly stop breathing during sleep. Sleep apnea may cause daytime sleepiness and make heart disease and stroke more likely.

Weight loss often improves sleep apnea.


System design: CQRS documents by Greg Young | My first 30 minute study

While a relatively simple process, this will solve many of the problems that existed in the stereotypical architecture. The service has been split into two separate services, a read side and a write side or the Command side and the Query side. 

This separation enforces the notion that the Command side and the Query side have very different needs. The architectural properties associated with use cases on each side are tend to be quite different. Just to name a few: 

Consistency 

Command: It is far easier to process transactions with consistent data than to handle all of the edge cases that eventual consistency can bring into play. 

Query: Most systems can be eventually consistent on the Query side. 

Data Storage 

Command: The Command side being a transaction processor in a relational structure would want to store data in a normalized way, probably near 3rd Normal Form (3NF) 

Query: The Query side would want data in a denormalized way to minimize the number of joins needed to get a given set of data. In a relational structure likely in 1st Normal Form (1NF) 

Scalability 

Command: In most systems, especially web systems, the Command side generally processes a very small number of transactions as a percentage of the whole. Scalability therefore is not always important. 

Query: In most systems, especially web systems, the Query side generally processes a very large number of transactions as a percentage of the whole (often times 2 or more orders of magnitude). Scalabilityis most often needed for the query side. 

Partitioning 

A very common performance optimization in today’s systems is the use of Horizontal Partitioning. With Horizontal Partitioning the same schema will exist in many places and some key within the data will be used to determine in which of the places the data will exist. Some have renamed the term to “Sharding” as of late. The basic idea is that you can maintain the same schema in multiple places and based on the key of a given row place it in one of many partitions. 

One problem when attempting to use Horizontal Partitioning with a Relational Database it is necessary to define the key with which the partitioning should operate. This problem goes away when using events. Aggregate IDs are the only partition point in the system. No matter how many aggregates exist or how they may change structures, the Aggregate Id associated with events is the only partition point in the system. 

Horizontally Partitioning an Event Store is a very simple process. 

Saving Objects 

When dealing with a stereotypical system utilizing a relational data storage it can be quite complex to figure out what has changed within the Aggregate. Again many tools have been built to help alleviate the pain that arises from this often complex task but is the need for a tool a sign of a bigger problem? 

Most ORMs can figure out the changes that have occurred within a graph. They do this generally by maintaining two copies of a given graph, the first they hold in memory and the second they allow other code to interact with. When it becomes time to save a complex bit of code is run, walking the graph the code has interacted with and using the copy of the original graph to determine what has changed while the graph was in use by the code. These changes will then be saved back to the data storage system. 

In a system that is Domain Event centric, the aggregates are themselves tracking strong events as to what has changed within them. There is no complex process for comparing to another copy of a graph, instead simply ask the aggregate for its changes. The operation to ask for changes is far more efficient than having to figure out what has changed. 

Loading Objects 

A similar issue exists when loading objects. Consider the work that is involved with loading a graph of objects in a stereotypical relational database backed system. Very often there are many queries that must be issued to build the aggregate. In order to help minimize the latency cost of these queries many ORMs have introduced a heuristic of Lazy Loading also known as Delayed Loading where a proxy is given in lieu of the real object. The data is only loaded when some code attempts to use that particular object. 

Lazy Loading is useful because quite often a given behavior will only use a certain portion of data out of the aggregate and it prevents the developer from having to explicitly represent which data that is while amortizing the cost of the loading of the aggregate. It is this need for amortization of cost that shows a problem. 

Aggregates are considered as a whole represented by the Aggregate Root. Conceptually an Aggregate is loaded and saved in its entirety. (Evans, 2001). 

Conceptually it is much easier to deal with the concept of an Aggregate being loaded and saved in its entirety. The concept of Lazy Loading is not a trivial one when added and is especially not trivial when optimizing use cases. The heuristic is needed because loading full aggregates from a relational database is operationally too slow. 

When dealing with events as a storage mechanism things are quite different. There is but one thing being stored, events. Simply load all of the events for an Aggregate and replay them. There can only ever be a single query on the system, there is no need to attempt to implement things like Lazy Loading. This is bad for people who want to build complex and quite often impressive frameworks for managing things like Lazy Loading but it is good for development teams who no longer need to learn these frameworks. 

Many would quickly point out that although it requires more queries in a relational system, when storing events there may be a huge number of events for some aggregates. This can happen quite often and a relatively simple solution exists for the problem. 

Rolling Snapshots 

A Rolling Snapshot is a denormalization of the current state of an aggregate at a given point in time. It represents the state when all events to that point in time have been replayed. Rolling Snapshots are used as a heuristic to prevent the need to load all events for the entire history of an aggregate. Figure 5 shows a typical Event Stream. One way of process thing the event stream is to replay the events from the beginning of time until the end of the event stream is reached.

There is not an impedance mismatch between events and the domain model. The events are themselves a domain concept, the idea of replaying events to reach a given state is also a domain concept. The entire system becomes defined in domain terms. Defining everything in domain terms not only lowers the amount of knowledge that developers need to have, it also limits the number of representations of the model needed as the events are directly tied to the domain model itself.

Business Value of the Event Log 

It needs to be made clear at the very start of this section that the value of the Event Log is directly correlated with places that you would want to use Domain Driven Design in the first place. Domain Driven Design should be used in places where the business derives competitive advantage. Domain Driven Design itself is very difficult and expensive to apply; a company will however receive high ROI on the effort if the domain is complex and if they derive competitive advantage from it. Using an Event Log similarly will have high ROI when dealing with an area of competitive advantage but may have negative ROI in other places. 

Storing only current state only allows to ask certain kinds of questions of the data. For example consider orders in the stock market. They can change for a few reasons, an order can change the amount of volume that they would like to buy/sell, the trading system can automatically adjust the volume of an order, or a trade could occur lowering the volume available on the current order. 

If posed with a question regarding current liquidity such as the price for a given number of shares in the market, it really does not matter which of these changes occurred, it does not really matter how the data got the way it was, it matters what it is at a given point in time. A vast majority of queries even in the business world are focused on the what, labels to send customers mails, how much was sold in April, how many widgets are in the warehouse. 

System design: Event sourcing and CQRS | CQRS pattern | 20 minutes study

 May 10, 2021

I like to read more about CQRS pattern. 

Event Sourcing and CQRS

Alexey Zimarev  |  17 June 2020

CQRS stands for Command-Query Segregation Principle. Greg Young described (and named) the pattern thoroughly in 2010, but the idea existed way before that time. On a high level, CQRS states the fact that operations that trigger state transitions should be described as commands and any data retrieval that goes beyond the need of the command execution, should be named a query. Because the operational requirements for executing commands and queries are very often different, developers should consider using different persistence techniques for handling commands and queries, therefore segregating them.

Operations vs Reporting

Most software systems need to persist data. Since for the scope of this guide we assume that the business logic of a software system is represented and executed by the Domain Model, we can say that application persistence focuses on persisting domain objects, like aggregates and entities. Because domain objects often have complex structures, developers might need to apply advanced persistence techniques to overcome the impedance mismatch. The Domain Model-oriented persistence is optimised for transactional performance, because the essential function of the Domain Model is to execute operations that trigger state transitions in the system, according to the logic and rules of the model.

Such an approach, however, could create challenges when it comes to retrieving data for reporting purposes.

Don’t see reporting as an act of creating a PDF or CSV file. Each query to the database that aims to get some data in order to show it to the user or return the result of an API call is also reporting.

The challenge surfaces from the fact that reporting needs are often drastically different from the needs of executing transactional operations. In addition, many systems have a clear imbalance between the number of writes and reads. For example, a typical user-facing application has much less writes than reads, because users normally read the information provided by the system and rarely execute operations in the systems. Some systems, however, have the opposite imbalance with prevailing writes. Think of a back-office of your bank, or a vehicle tracking system as examples.

One of the first traces of the original discussion around the reporting issue when using the Domain Model pattern happened during the The challenge of executing DDD panel with Martin Fowler, Eric Evans and others, back in 2004. Also in 2004, Martin Fowler published the ReportingDatabase pattern in Bliki. The pattern suggests using a separate database for reporting purposes and to push changes from the operational database to that reporting database.

ReportingDatabase

Source: Martin Fowler’s bliki

The Reporting Database pattern is similar to CQRS but not exactly the same. It aims to solve the similar issue, but gets confused with the term reporting. In the Reporting Database pattern context, reporting has a more traditional meaning.

The kick-off question of the aforementioned panel discussion was:

On the other hand business execs want / crave / require “ad-hoc” reporting, with the degree of “ad-hoc-ness” varying from simple reports to data warehouses. You’d like to simply write some SQL and stuff the results into a report, but this is impossible when you have the domain model supporting all of your complex logic. So, what do you do?

Here, reporting only covers something that is accessed now and then. Such a report is only actual at the moment it gets produced, just as a printed report.

CQRS, however, assumes that any query is some sort of report, whether it is moved, printed out, or shown to the user inside the application.

Commands vs Queries

Developers often confuse CQRS with the Reporting Database pattern, although there is nothing in the name (Command-Query Responsibility Segregation) to suggest that.

The essence of CQRS is to separate command flow from the query flow, based on fundamental characteristics of those concepts.

Commands

The main attribute of a command is that when the command gets successfully executed, the system transitions to a new state.

CommandFlow

Another important attribute of a command is that it conveys the intent of the user. Requests like UpdateCustomer are not exactly commands according to CQRS. Within the application boundaries, handling a command should result in one transaction on one Aggregate.

As a consequence, the command flow in CQRS goes hand in hand with the Task-Based User Interface pattern. In contrast with the CRUD-based user interface, which has four basic operations available for users - Create, Edit, Save and Delete, a task-based UI makes each operation explicit, like Check Out, Add Item or Cancel Order. By doing this, it makes operations available to the user easily translatable to commands that the UI sends to the Domain Model via an API.

Queries

Queries, as the name suggest, allow getting data from the persistent store, so it can be shown to the user, sent to another system or used for any other purpose.

QueryFlow

Although the image shows all the system elements stored in what looks like a single database, it is just an example and doesn’t have to be that way.

Unlike commands, queries do not need to involve the Domain Model because queries do not execute any operations and should not contain any business logic. In addition, queries have no side effects and are completely idempotent, so it doesn’t matter how many times the query gets executed - it will always return the same result, unless the system state changed in the meantime.

Therefore, when implementing a query handler, developers don’t need to use the same way to access data as they do when persisting domain objects. For example, if the Domain Model persistence uses some ORM framework that distribute state of domain objects across tables in a relational database, a query could fire an SQL statement that ignores the ORM and gets the data directly from those tables.

CQRS also suggests that queries target specific use cases and return a pre-composed data set that can be shown in its entirety on the screen or in a cohesive part of the screen.

CQRS with Event Sourcing

Back in 2016 in his talk A Decade of DDD, CQRS, Event Sourcing Greg Young said the following:

“You need to look at CQRS not as being the main thing. CQRS was a product of its time and meant to be a stepping stone towards the ideas of Event Sourcing.”

It is not a coincidence that Greg proposed the CQRS pattern at the same time as he introduced Event Sourcing to the public. Unlike state-based persistence, where there might be a way to avoid using the domain model for queries, such an approach is hard to impossible to use in event-sourced systems. It is because there is no place where the state of a domain object is stored entirely.

As described in the previous article, domain entities in event-sourced systems are stored as event streams, essentially each entity is a sequence of events from the persistence point of view. There is more to it yet. Domain events in event store alone do not allow reconstructing the entity state without knowing the logic that the entity uses to rehydrate its own state from events and this logic is in the code of the Domain Model.

Projections

We need a way to project events to an alternative store, which we can easily query. It could be a relational or document database, or cache, or any other type of persistence that is applicable for a specific use case.

Projection

The idea is that a software component called a Projection subscribes to the live event feed of the events database and when it receives an event, it could project the information in that event to a query model in a dedicated reporting database.

In the context of CQRS you often hear write side, read side and read model. The write side is the command side, since that’s the place where state mutations, also referred as writes, take place. By comparison, the query side is often being referred as the read side, and the query model gets by name read model.

By definition, a projection is a representation of an object using a different perspective. For example, isometric and orthographic projections allow us to represent a 3D object on paper using different points of view.

Geometry

When we talk about projecting data, we usually mean representing the data differently from the form it was originally stored. For example, a relational database view is a projection. It uses a transformation query to represent a subset of data that is already available in the database, producing a derived result from that data. The view, however, is stateless and has no side effects - it doesn’t change the original data in any way.

Projecting events

The same applies to event projections with one difference. When projecting events, we have to store the state, so we have the latest snapshot of the projected state. It works somewhat similarly to materialized views that some relational databases support. Still, outside of its own state, such a projection doesn’t manipulate the original data (events) in any way.

In CQRS, the query side is always a projection, even if it is not implemented as a stateful manner. When it comes to projecting events, we really want to keep the state of our read models in a queryable store to solve the issue of events streams being hard to query ad-hoc.

The idea is that the projection will receive all the events that it is able to project and will do the normal CRUD operations on the read model it controls, using the normal CRUD operations provided by the read model database (which could be anything).

EventsProjection

Projections do the same thing as the When function in the entity code. Just as the entity state, the read model state is the left fold of all the event it processes.

Unlike the entity state, which is only applying events for that single entity, projections aren’t limited to only process events of a single entity and can assemble and aggregate data for multiple entities, even for different types of entities.

Keeping all this in mind, we can write code for a simple projection.

public class OrderOverviewProjection {
    Database db;

    OrderOverviewProjection(Database db) => this.db = db;

    public void Project(object event) {
        var dbOp = GetOperation(event);
        db.Execute(dbOp);
    }

    DbOperation GetOperation(object event) {
        return event switch {
            OrderCreated e => new CreateOperation(
                new OrderOverview(e.OrderId, e.CustomerId)),
            ItemAdded e => Update(e.OrderId).With(x => x.Items.Add(MapItem(e.Item))),
            PaymentRequested e => Update(e.OrderId).With(x => x.Status = AwaitingPayment),
            PaymentReceived e => Update(e.OrderId).With(x => x.Status = Paid)
        }

        UpdateOperation<OrderOverview> Update(string id)
        {
            return new UpdateOperation<OrderOverview>(id);
        }
    }
}

Projections are usually dependent on specific infrastructure interfaces, since they work directly with databases that keep their read models.

Subscriptions

Since the query model is used almost every time the application handles any type of query, whether a GET HTTP request or something else, we must ensure that the information stored in the read model is up-to-date. Therefore, we need to establish a real-time connection to the event store, so the projection receives events immediately after they are stored.

Event Store in particular provides a way to achieve that requirement. The most common implementation for client-side projections is to use catch-up subscriptions. The term “catch-up” comes from the fact that such subscriptions, when first connected to the server, will read all the historical events (catch up) and then automatically switch to real-time event processing.

With the Event Store .NET client, you can create a subscription like this:

// Presumably we got an IEventStoreConnection instance from somewhere

var subscription = connection.SubscribeToStreamFrom(
    stream: "mystream",
    lastCheckpoint: StreamCheckpoint.StreamStart,
    settings: CatchUpSubscriptionSettings.Default,
    eventAppeared: EventAppeared);

When you call the SubscribeToStreamFrom method, the subscription activates immediately. The EventAppeared function will be called for each event that already exists in the mystream stream. When all the historical events will be processed, the subscription will get to the real-time mode and starts receiving events as they are appended to the stream.

The AppendStream function could look like this, if we want to use our projection:

Task EventSppeared(EventStoreCatchUpSubscription _, ResolvedEvent evt) {
    var domainEvent = DeserialzeEvent(resolvedEvent);
    projection.Project(domainEvent);
}

Of course, there’s more plumbing involved in places like the DeserializeEvent function, which isn’t covered here, since the aim of this article is to describe the concept.

Checkpoints

The SubscribeToStreamFrom function requires you to specify the stream checkpoint in the lastCheckpoint parameter. In the example above, we used the StreamCheckpoint.StreamStart constant, which instructs the subscription to start reading events from the beginning of time (for that stream).

It will work, but it’s not practical. When an application that hosts this subscription eventually stops and then starts again, the subscription will start catching up from the first event in the stream again. It defeats the purpose of having the read model state persisted in a database. If it’s feasible for a system to re-project all the events each time the projection starts, we could just keep all the read models in memory.

It is, actually, a valid technique for caching and keeping the aggregate state snapshot available for command processing. Keep in mind how much time will be required to read all the events at the startup.

In order to avoid re-projecting the whole history all over again, we can store the event offset (a position the event in the stream) after projecting the event. By doing that, we let the system to load the stored checkpoint when the application starts again, so it can subscribe from the last known position instead of the stream start.

With this in mind, the previous code snippet can be refactored to handle the checkpoint as well.

// Startup code
var lastCheckpoint = checkpointStore.Load("mystream-checkpoint");
var subscription = connection.SubscribeToStreamFrom(
    stream: "mystream",
    lastCheckpoint: lastCheckpoint ?? StreamCheckpoint.StreamStart,
    settings: CatchUpSubscriptionSettings.Default,
    eventAppeared: EventAppeared);

In addition to the checkpoint loading code, we need to persist the checkpoint for processed events.

Task EventSppeared(EventStoreCatchUpSubscription _, ResolvedEvent evt) {
    var domainEvent = DeserialzeEvent(resolvedEvent);
    projection.Project(domainEvent);
    checkpointStore.Save(evt.OriginalPosition.Value);
}

Now, when the application suddenly stops and then starts again, it will load the last known checkpoint and will subscribe from that stream position.

The code snippet above uses two database operations for each processed event. It might introduce an issue known as two-phase commit that has a possibility to execute only the first operation (read model update) but not the second (store the checkpoint) due to some transient failure. There are two ways to overcome the issue:

  • Use a database transaction to wrap both operations.
  • Make projections idempotent, so applying the same event twice won’t bring the read model to an invalid state.

This is the basics of projections but might’ve noticed that subscribing to a single stream won’t deliver much of a value. Projections normally handle events of many entities. This is where the $all stream becomes important to create projections that can handle events from multiple streams.

Global event stream

Subscribing to a single event stream that represents an entity isn’t always useful. More often than not the projection code needs to handle any event that comes from the domain model and build sophisticated read models that serve a variety of needs.

For example, we might need to show user a page that contains order details, including payment and shipping information. It might be that the domain model splits those concerns to different aggregates with an independent life cycle, since they address different concerns. One common approach is to compose such a page on the front-end side and call multiple API endpoints to collect the information from different parts of the system. Albeit such an approach can be useful, with Event Sourcing we have an option to build a read model, which would represent all the information for that page. By doing so, the need to call multiple API endpoints and do somewhat complex data composition in the UI disappears.

Conceptually, to achieve the goal like that, a projection needs to receive events from different streams.

MultiStreamProjection

The projection will then create a read model for each order and project both order information and payment information to it.

In this case, the subscription that feeds the projection with events has to subscribe to some stream that contains all the events from entities of different types, with different ids.

‘$all’ stream

In Event Store, the concept of individual stream builds on top of the single event sequence, which is called the $all stream.

Streams with names starting with $ are considered system streams, but it doesn’t mean you can’t use them.

It means that each new event gets appended to the global event sequence. The stream name for that event serves for event indexing, so you can read a subset of events by using the stream name. However, the stream name doesn’t tell Event Store where the event needs to be persisted, since all the events go to the global append-only store. Because of this internal structure of the persistence, Event Store allows subscribing to the global stream of events. Client libraries have a special method for subscriptions to connect to that stream.

var subscription = connection.SubscribeToAllFrom(
    lastCheckpoint: lastCheckpoint ?? AllCheckpoint.AllStart,
    settings: CatchUpSubscriptionSettings.Default,
    eventAppeared: EventAppeared);

The API for the SubscribeToAllFrom is almost identical to SubscribeToStreamFrom but the checkpoint is not represented as a long integer number but as a struct with two long integers - prepare position and commit position. Therefore, the checkpoint store needs to support storing the Position struct instead of a simple number.

The most useful thing about the global event stream is that all events in it are ordered and any subscription that uses the $all stream will get events in the same order as they were written to Event Store, even if events were appended to different logical streams. That way projections can be reassured that things that must happen in order will do so, as long as the write-side of the application behaves correctly.

The illustration above shows that one projection handles events for two different event types to construct the read model with the full order overview. In order for the projection to handle payment events, those events need to contain the order id, so the projection could understand which overview record needs updating, since those records are created for each individual order.

Read model scope

When developers come to Event Sourcing with a substantial experience of persisting domain objects in traditional databases, they rarely resist an urge to build projections that build the current entity state as a read model. By doing so, they return to the comfort zone of being able to check the current state of any domain object, at any time by looking into the database. However, such an approach doesn’t really deliver the full power of CQRS combined with Event Sourcing.

One of the great outcomes of having an event-sourced system is the ability to create new read models at will, at any time, without affecting anything else. For example, if you find your read models in MongoDB to be not fitting the full-text search requirement, you can build a new projection targeting ElasticSearch with a limited set of fields that the full-text search function needs. Similarly, it is possible to build new aggregations or sets of denormalized data with the goal to have pre-calculated pages available for the UI to show, instead of running expensive queries every time a user opens that page.

Continuing the previous illustrations and snippets, our eCommerce system might one day require a page that shows all the orders for one single customer. It is possible to build an API endpoint that runs a query for that purpose. There are drawbacks for adding queries though since each query potentially introduces side effects on the database, like space used for indexes, degraded performance and so on. Some databases won’t even let you query without changing the persistence model significantly. Building a new read model, however, is relatively straightforward.

ProjectionNewAggregation

One thing to remember is that all the events included in such aggregation need to have a field that conveys the aggregation id (customer id in this case). It might be problematic if the original event schema didn’t contain enough data. It’s not exactly straightforward, but this problem has several solutions, like stream joins (enrichment), upcasting, migration and so on. Those patterns are out of scope for this article.

Here are some references for further reading:


Photo of Alexey Zimarev

Alexey Zimarev Alexey is a developer advocate for Event Store and the author of “Hands-on Domain-Driven Design with .NET Core” book. His particular interests are in event-driven architectures, domain-driven design, distributed systems, and event-sourcing. Alexey contributes to open-source software as a member of MassTransit and Proto.Actor projects, and a maintainer of RestSharp. He manages the Domain-Driven Design Norway meetup group and speaks at technical conferences. Follow Alexey on Twitter at @Zimareff.


System design: CQRS pattern | In Chinese | CQRS亂談

 May 10, 2021

Here is the article. 

在做平台的促銷活動時, 總是想要查詢任何一個商品的歷史狀態資料, 而且任何時間都能生成統計數據. 想要查詢上個月份、上一季或者過去任何時間的彙總.
一些7788的表示方式查詢方式...(客戶跟PM的需求, 我們總是難以滿足)

有些查詢方式, 關聯式資料庫查詢起來不太高效.
有些查詢方式, NoSQL也不好查.
有的還要查詢稽核日誌或者某一隻股票的變化歷程等等的...

一般作法如圖: 參考連結

通常我們會把對model做CRUD, 並且儲存在資料庫內.
但隨著業務越來越複雜時, 就引入分層模式.
開始讀取出來是DAO, 在業務邏輯層變成DTO, 再給表達層...
為的是什麼?
因為表達層所要的展示方式已經跟當初寫入在資料庫的模型有所不同了.
為了使用多種模型來表示相同的數據.

再來這樣的模式也導致一些問題.
在相同的資料上一起執行讀寫作業時, 就有鎖的問題了(事務隔離).
有的還要排序、分頁...
使用ORM又Preload很多資料為了提高吞吐量, 但卻佔用了API服務的大量記憶體.
如果查詢很複雜跨很多表, 就出現大量Join...性能就下降.

為了讓讀、寫分開. 有的組織對資料庫做了讀寫分離架構.
讓Read命令去副本讀取, CUD命令去主庫寫入操作.
但這樣依然無法解決多種表達層模型還有複雜Join的問題.

CQRS

CQRS(Command Query Responsibility Segregation)
Martin Fowler在2011年部落格上曾經介紹了CQRS(命令查詢職責分離), 是一種基於讀寫分離思想的架構模式.
把一個系統分成寫入命令, 跟查詢指令兩部份.
寫入命令, 當收到CUD請求時, 將之轉成Domain Event, 並寫到Event Store.

讀取指的就是表達層所需要的資料

事件傳遞的方式, 常見的還是用之前提到的MQ

Components

  1. Command Handler: 就是實際的業務邏輯, 驗證, 或是處理Request Body, 然後也負責轉成Event, 寫入DB, 推送到MQ

  2. Event Handler: 這裡就是負責訂閱特定地Domain Event, 然後轉成查詢時需要的DTO寫到適當的儲存中(RDBMS、Mongo、ES...etc)

  3. Facade Pattern: 提供一組抽象的API接口, 給表達層用

  4. Read Layer: 就負責查詢儲存, 並且組合

pros

寫入性能更好(畢竟讀寫算是分離了)
讀取性能更好(因為會用適當的儲存做查詢)
歷史紀錄追蹤
滿足單一職責, 因為都滿足了對各自的業務做封裝
兩邊允許各自擴展
查詢端的儲存已經是存查詢所返回的DTO了, 不必再做Model->DTO的轉換

cons

架構複雜
資料同步需要一些時間, 但保障最終一致性
很需要DDD的相關領域知識

適用情境

  1. 當寫入命令的模型跟讀取模型的差異很大時
  2. 希望系統查詢跟寫入分開優化演化
  3. 希望系統同時滿足高併發寫、高併發讀取時, 且允許資料最終一致性

Aggregate

這名詞來自於DDD, 指的是Entity和Value Object的組合.
它定義了一個邊界, 在這邊界裡, 資料必須保證資料完整性.
Aggregate有一個Root Entity, 可以從這Root去獲取該邊界內的其他entity和value object.
如果資料庫有做水平分片(sharding), 那同一個聚合內的entity是在同一個分片內.

這裡的Aggregates之間主要是透過標示符號(例如資料庫的PK), 而不是直接對象引用.
像是Order聚合內有UserID, 透過UserID去關聯到User聚合.

參考1
參考2
Greg Young文章
Martin Fowler文章

PS: CQRS不是銀彈...適用的場景不多, 若硬要套用, 只能默哀3秒鐘先.

AWS online tech talks: Amazon EMR Masterclass

May 10, 2021

Here is the link. 

Amazon EMR enables fast processing of large structured or unstructured datasets, and in this recorded webinar we'll show you how to setup an Amazon EMR job flow to analyse application logs, and perform Hive queries against it. Also, we will review best practices around data file organisation on Amazon Simple Storage Service (S3), how clusters can be started from the AWS web console and command line, and how to monitor the status of a MapReduce job. Finally we take a look at Hadoop ecosystem tools you can use with Amazon EMR and the additional features of the service. View and download the slides from this webinar on Slideshare here: http://www.slideshare.net/AmazonWebSe...


Sunday, May 9, 2021

How To Start Running When You're Overweight

May 9, 2021

Here is the link. 

Here's how to start running when you're overweight. Losing weight can feel extremely daunting as a task, and as such keeps even the most strong-willed people from getting started. While it may feel too intimidating to walk into a gym without a plan or self-confidence, it's never too early to simply start moving. Coach Holly breaks down her best tips for getting started safely and making it stick. Consider this the start of an awesome journey! Get started with 2 FREE WEEKS of training down below: http://therunexperience.com/free-quic...