OLTP Và OLAP Có Gì Khác Nhau?

Trong việc phát triển cơ sở dữ liệu quan hệ thường có hai chiến lược chính là OLTP (On-line transactional processing) và OLAP (On-line analytical processing). Vậy chúng là gì, giống nhau và khác nhau như thế nào? Chúng ta hãy cùng tìm hiểu thông qua một hệ thống thực.

OLTP là gì?

OLTP là viết tắt của On-line transactional processing, khái niệm này không khó để nắm bắt. Các hệ thống OLTP là các hệ thống “cổ điển” xử lý các giao dịch dữ liệu. Chúng đang ở xung quanh bạn. Trong ngân hàng, máy ATM hoặc hệ thống máy tính được nhân viên ngân hàng sử dụng để ghi lại một giao dịch là một hệ thống OLTP, thường là một cơ sở dữ liệu. Nếu bạn nhắn tin cho ai đó từ điện thoại thông minh, bạn đang làm việc với một hệ thống OLTP khác. Sổ tiền mặt tại siêu thị lại chạy trên một hệ thống OLTP khác nữa.
Tóm lại, hầu hết các ứng dụng kinh doanh đều là các hệ thống OLTP. Thật khó để tưởng tượng một lĩnh vực computer engineering mà không có một số hình thức của OLTP. Trên thực tế, toàn bộ ngành khoa học máy tính đã lớn lên xung quanh nhu cầu xử lý giao dịch một cách có trật tự.
Do đó chúng ta đi đến phần quan trọng của từ viết tắt trong OLTP: T, viết tắt của transactional. Các giao dịch rất quan trọng và việc giữ chúng một cách có tổ chức cũng rất quan trọng.
Phải làm gì để giữ cho các giao dịch có tổ chức? Đó là các cơ sở dữ liệu giao dịch phải ổn định (không dễ thay đổi), độc lập, nhất quán và nguyên tử (không dễ phân chia).
Nói một cách đơn giản, loại giao dịch này đảm bảo rằng các hoạt động được thực hiện bởi những người dùng khác nhau không can thiệp vào nhau. Chẳng hạn, nếu vợ chồng mỗi người rút tiền từ tài khoản ngân hàng chung, các giao dịch phải đảm bảo rằng họ không rút tiền nhiều hơn tài khoản của mình.
Một hệ thống OLTP cần các thuộc tính này để đảm bảo rằng mọi thứ đang hoạt động một cách có trật tự. Khi bạn sử dụng ngân hàng, bạn tin tưởng rằng số tiền bạn gửi vào tài khoản của bạn, không bị lẫn lộn với các giao dịch khác của bạn hay bị mất.
Rõ ràng, quá trình xử lý giao dịch là rất quan trọng đối với toàn xã hội của chúng ta.

Còn OLAP là gì?

OLAP là viết tắt của On-line analytical processing. Chữ cái chìa khóa ở đây là analytical cũng cho chúng ta biết hệ thống OLAP làm gì. Một hệ thống OLAP được dùng để phân tích dữ liệu một cách hiệu quả.
Không giống như OLTP, các hệ thống OLAP làm việc với một lượng dữ liệu rất lớn. Bảo đảm tính chính xác và tính toàn vẹn của các giao dịch không phải là mục đích của chúng; điều này trái ngược với OLTP. OLAP là ở đây để cho phép chúng ta tìm ra xu hướng, điểm mấu chốt  trong một big picture. Các hệ thống OLAP có một nhóm người dùng nhỏ hơn so với các hệ thống OLTP, thường là những nhà phân tích hoặc quản lý. Ví dụ: bạn sẽ không tương tác với hệ thống OLAP của ngân hàng vì nó ghi lại các giao dịch tài khoản của bạn.
Hoạt động chủ yếu trong các các hệ thống OLAP là các truy vấn dữ liệu, Chúng thường là các truy vấn lớn và mất nhiều thời gian để thực hiện. Ngược lại, các hệ thống OLTP sử dụng các lệnh phổ biến như INSERT và DELETE. Và chúng không nên quá lâu.
Kích thước của hệ thống cũng là một điểm khác nhau cơ bản giữa các hệ thống OLTP và OLAP. OLTP phải ổn định và nhanh chóng (để thực hiện các công việc thời gian thực), trong khi OLAP phải đủ lớn và đủ mạnh để lưu trữ tất cả các dữ liệu kinh doanh.

Một ví dụ OLTP thực tế

Chúng ta hãy xem xét sự khác biệt giữa OLAP và OLTP bằng cách sử dụng các ví dụ thực tế.
Hãy nhớ rằng các truy vấn OLTP đơn giản và nhỏ hơn nhiều so với những gì được sử dụng trong các hệ thống OLAP. Chúng ta cần phải thực hiện các truy vấn OLTP một cách nhanh chóng với số row nhỏ. Ví dụ này sẽ xem xét mô hình thanh toán tài chính OLTP đơn giản.
Chúng ta có các bảng dữ liệu customercontract và payment. Cùng với đó là các bảng quan hệ và các bảng detail như trong hình dưới.
Giả sử chúng ta cần phải chèn một khoản thanh toán là $5.000 vào số tài khoản 1112 cho một khách hàng có ID là 11. Điều gì sẽ xảy ra?
Khi giao dịch này đang được xử lý, có nhiều giao dịch khác đang được thực hiện cùng một lúc. Trong một hệ thống OLTP được thiết kế đúng cách, điều này không có vấn đề. Không có xung đột nào xảy ra, tất cả mọi thứ đang tiến hành một cách tuần tự.
Bây giờ chúng ta hãy xem một truy vấn OLTP điển hình, hiển thị dữ liệu có liên quan đến người dùng. Giả sử ai đó có ID khách hàng là “11” muốn xem 10 giao dịch cuối cùng của họ:
Hoặc có thể cùng khách hàng đó muốn xem thông tin về hợp đồng có trong hồ sơ của tổ chức tài chính:
Điều quan trọng chúng ta nhận thấy ở đây là các truy vấn nhỏ, nhanh và nguyên tử.

Và một ví dụ OLAP thực tế

Chúng ta hãy nhìn ví dụ trên ở một view khác, OLAP. Nó được thiết kế ở dạng Star đơn giản và được dùng để hỗ trợ báo cáo và phân tích.
Trong model này chúng ta sử dụng một bảng fact trung tâm fact_balance  chứa thông về balance của khách hàng theo ngày, theo hợp đồng và ba bảng dimension dim_customer, dim_contract, dim_date.
Dữ liệu được đưa vào mô hình OLAP không giống như các mô hình OLTP. Thay vì thu thập thông tin từng chút một trong từng lần cập nhật giao dịch, OLAP được lấp đầy bởi các truy vấn theo lô, quét toàn bộ model nguồn (thường là một hệ thống OLTP) và nhập dữ liệu vào mô hình OLAP. Rõ ràng, các câu lệnh INSERT cho OLAP lớn hơn và chậm hơn bởi vì chúng bao gồm nhiều bảng nguồn. Quá trình đưa dữ liệu vào hệ thống OLAP từ các hệ thống nguồn được gọi là ETL (extract-transform-load).
Chúng ta hãy tạo một truy vấn ETL đơn giản để đưa dữ liệu vào các bảng của mô hình OLAP. Chẳng hạn với bảng dim_customer:
Như các bạn thấy, chúng ta đang sử dụng nhiều bảng để đưa dữ liệu vào một bảng dimension. Các truy vấn phức tạp thường xuyên xảy ra đối với các hệ thống OLAP.
Chúng ta có thể đưa dữ liệu vào bảng dim_contract theo cách tương tự. Tuy nhiên, chúng ta sẽ không đưa dữ liệu vào bảng dim_date, vì nó được xác định trước trong cơ sở dữ liệu và ngày tháng của nó không thay đổi. Đối với bảng fact_balance, quá trình điền thông tin phức tạp hơn rất nhiều. Tất cả những gì chúng ta cần biết là fact_balance chứa số dư tài khoản vào các khoảng thời gian cụ thể.
Nếu như OLTP là một tuyến đường chạy trơn tru với nhiều loại xe tương tự nhau, thì tuyến đường OLAP là một nơi hỗn độn với những chiếc xe lạ lùng đang di chuyển ở các tốc độ khác nhau. Nó không thống nhất trong bất kỳ khía cạnh nào: có những chiếc xe ô tô to lớn mà không ai nhìn thấy trước đó (truy vấn ad-hoc), xe ô tô chậm và lớn mà chúng ta thấy thường xuyên (báo cáo hàng tháng) và thậm chí có những con thú khác.
Một ví dụ điển hình của truy vấn OLAP là đưa ra số dư trung bình mỗi quý cho tất cả người dùng:
Một truy vấn OLAP khác có thể hiển thị thông tin tổng hợp của tất cả khách hàng sinh ra sau năm 1990:
Các truy vấn OLAP thường ở dạng tổng hợp thông tin của tất cả dữ liệu của một tập hợp con cụ thể. Chẳng hạn, chúng ta đang tìm kiếm các xu hướng, giá trị trung bình, tổng hợp … Các truy vấn này có thể mất rất nhiều thời gian, tùy thuộc vào kích thước của tập hợp con mà chúng ta đang khám phá và do đó không thống nhất (không giống như các OLTP queries).
Tôi hy vọng rằng bài viết này đã giúp bạn hiểu được sự khác nhau của các hệ thống OLTP và OLAP và cả cách chúng làm việc cùng nhau. Chúng không phủ định nhau mà là một cặp tồn tại song song, hỗ trợ, bù trừ cho nhau.

Building CRUD Web Application using MERN Stack

A comprehensive step by step tutorial of building create-read-update-delete (CRUD) web application from scratch using MERN (Mongo, Express, React, Node.js) stack. One of the most popular web application stack based on Node.js is MERN stack. MERN is the abbreviation of MongoDB, Express.js, React.js, and Node.js, almost the same with the MEAN stack except MEAN stack using Angular and MERN stack using React. This basic step by step tutorial of MERN stack is building CRUD (Create-Read-Update-Delete) web application. 


Table of Contents:



React can integrate with MERN stack as the front end that can access data from Express and MongoDB by consuming RESTful API. In this scenario of MERN Stack React will accessing data from Express and MongoDB by HTTP calls as default React component. We are using the same application for Express and React that run on the same IP and PORT. Before starting the tutorial, the following tools and framework are required:
Node.js (Stable build recommended)
Express.js
MongoDB
Mongoose.js
React.js
We assume that you have already Installed Node.js and MongoDB. Make sure Node.js command line is working (on Windows) or runnable in Linux/OS X terminal. You can run MongoDB in the different terminal or command line.


1. MERN Stack: Install and Create React App

Open the terminal or Node.js command line then go to your MERN projects folder. We will install React app creator for creating a React app easily. For that, type this command.
sudo npm install -g create-react-app
Now, create a React app by type this command.
create-react-app mern-crud
This command will create a new React app with the name `mern-crud` and this process can take sometimes because all dependencies and modules also installing automatically. Next, go to the newly created app folder.
cd ./mern-crud
Now, run the React app for the first time using this command.
npm start
It will automatically open the default browser the point to `http://localhost:3000`, so the landing page should be like this.
Building CRUD Web Application using MERN Stack - React Home Page


2. MERN Stack: Install and Configure Express.js

We have to replace the existing Node server that holds the default React application by Express.js as the backend. Stop the server by push keyboard Ctrl+C. Type this command to install Express.js and required dependencies.
npm install --save express body-parser morgan body-parser serve-favicon
Create a folder with the name "bin" and add a file with the name "www" on the root of the project.
mkdir bin
touch bin/www
Open and edit `bin/www` file then add this lines of codes.
#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('mean-app:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}
Now, open and edit "package.json" then replace "start" value.
"scripts": {
  "start": "node ./bin/www",
  "build": "react-scripts build",
  "test": "react-scripts test --env=jsdom",
  "eject": "react-scripts eject"
}
Next, create "app.js" at the root of the project folder.
touch app.js
Open and edit app.js then add this lines of codes.
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var bodyParser = require('body-parser');

var book = require('./routes/book');
var app = express();

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({'extended':'false'}));
app.use(express.static(path.join(__dirname, 'build')));

app.use('/api/book', book);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
Create routes for the book.
mkdir routes
touch routes/book.js
Open and edit “book.js" then add this lines.
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.send('Express REST API');
});

module.exports = router;
Now, open and edit `src/registerServiceWorkers.js` then add to the register function this `fetch` event listener below `load` event listener.
window.addEventListener('fetch', (event) => {
  if ( event.request.url.match( '^.*(\/api\/).*$' ) ) {
    return false;
  }
});
So, the complete register function will look like this.
export default function register() {
  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
      return;
    }

    window.addEventListener('load', (event) => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

      if (!isLocalhost) {
        // Is not local host. Just register service worker
        registerValidSW(swUrl);
      } else {
        // This is running on localhost. Lets check if a service worker still exists or not.
        checkValidServiceWorker(swUrl);
      }
    });

    window.addEventListener('fetch', (event) => {
      if ( event.request.url.match( '^.*(\/api\/).*$' ) ) {
        return false;
      }
    });
  }
}
Now, check the configuration by running again the Node server that handles by Express now.
npm start
Open the browser then go to `http://localhost:3000` then you will see the previous React home page. Then change the address to `http://localhost:3000/api/book`. You will see this response of web page.
Building CRUD Web Application using MERN Stack - Express RESTful API
That means it works.

3. MERN Stack: Install and Configure Mongoose

The part of MERN stack is MongoDB, to get connected to MongoDB from Node.js application we are usually using Mongoose.js as MongoDB object modeling. On the terminal type this command after stopping the running Express server.
npm install --save mongoose bluebird
Open and edit `app.js` then add this lines after another variable line.
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
mongoose.connect('mongodb://localhost/mern-crud', { useMongoClient: true, promiseLibrary: require('bluebird') })
  .then(() =>  console.log('connection succesful'))
  .catch((err) => console.error(err));
You can test the connection to MongoDB by run again the Node application and you will see this message on the terminal.
connection succesful
If you are still using built-in Mongoose Promise library, you will get this deprecated warning on the terminal.
(node:42758) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
That's the reason why we added `bluebird` modules and register it as Mongoose Promise library.


4. MERN Stack: Create Mongoose.js Model

Add a models folder on the root of the project folder for hold Mongoose.js model files.
mkdir models
Create a new Javascript file that uses for Mongoose.js model. We will create a model of Book collection.
touch models/Book.js
Now, open and edit that file and add Mongoose require.
var mongoose = require('mongoose');
Then add model fields like this.

var BookSchema = new mongoose.Schema({
  isbn: String,
  title: String,
  author: String,
  description: String,
  published_date: { type: Date },
  publisher: String,
  updated_date: { type: Date, default: Date.now },
});
That Schema will mapping to MongoDB collections called book. If you want to know more about Mongoose Schema Datatypes you can find it here. Next, export that schema.
module.exports = mongoose.model('Book', BookSchema);


5. MERN Stack: Create Routes for Accessing Book Data via Restful API

Open and edit again "routes/book.js” then replace all codes with this.
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Book = require('../models/Book.js');

/* GET ALL BOOKS */
router.get('/', function(req, res, next) {
  Book.find(function (err, products) {
    if (err) return next(err);
    res.json(products);
  });
});

/* GET SINGLE BOOK BY ID */
router.get('/:id', function(req, res, next) {
  Book.findById(req.params.id, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

/* SAVE BOOK */
router.post('/', function(req, res, next) {
  Book.create(req.body, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

/* UPDATE BOOK */
router.put('/:id', function(req, res, next) {
  Book.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

/* DELETE BOOK */
router.delete('/:id', function(req, res, next) {
  Book.findByIdAndRemove(req.params.id, req.body, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

module.exports = router;
Run again the Express server then open the other terminal or command line to test the Restful API by type this command.
curl -i -H "Accept: application/json" localhost:3000/api/book
If that command return response like below then REST API is ready to go.
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 2
ETag: W/"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w"
Date: Sat, 28 Oct 2017 22:39:59 GMT
Connection: keep-alive
Now, let's populate Book collection with initial data that sent from RESTful API. Run this command to populate it.
curl -i -X POST -H "Content-Type: application/json" -d '{ "isbn":"1783551623, 9781783551620","title":"React.js Essentials: A Fast-paced Guide to Designing and Building Scalable and Maintainable Web Apps With React.js","author": "Artemij Fedosejev","description":"A fast-paced guide to designing and building scalable and maintainable web apps with React.js.","published_date":"2015-08-27T16:00:00Z","publisher":"Packt Publishing" }' localhost:3000/api/book
You will see this response on the terminal if success.
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 459
ETag: W/"1cb-G9luzsvo07EKg3s00VUyB19WpA4"
Date: Thu, 02 Nov 2017 00:01:22 GMT
Connection: keep-alive

{"__v":0,"isbn":"1783551623, 9781783551620","title":"React.js Essentials: A Fast-paced Guide to Designing and Building Scalable and Maintainable Web Apps With React.js","author":"Artemij Fedosejev","description":"A fast-paced guide to designing and building scalable and maintainable web apps with React.js.","published_date":"2015-08-27T16:00:00.000Z","publisher":"Packt Publishing","_id":"59fa6052e245b0060c9aa5d5","updated_date":"2017-11-02T00:01:22.058Z"}


6. MERN Stack: Add React Router DOM

Our front end consists of Booklist, detail, create and edit. For navigating between that component, we need to create a route. First, install modules that required by the components.
npm install --save react-route-dom
npm install --save-dev bootstrap
npm install --save axios
Next, open and edit `src/index.js` then replace all codes with this.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../node_modules/bootstrap/dist/css/bootstrap-theme.min.css';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import Edit from './components/Edit';
import Create from './components/Create';
import Show from './components/Show';

ReactDOM.render(
  <Router>
      <div>
        <Route exact path='/' component={App} />
        <Route path='/edit/:id' component={Edit} />
        <Route path='/create' component={Create} />
        <Route path='/show/:id' component={Show} />
      </div>
  </Router>,
  document.getElementById('root')
);
registerServiceWorker();
You see that Edit, Create and Show added as the separate component. Bootstrap also included in the import to make the views better. Now, create the new edit, create and show files.
mkdir src/components
touch src/components/Create.js
touch src/components/Show.js
touch src/components/Edit.js


7. MERN Stack: Add List of Book to Existing App Component

As you see in the previous step that App component act as home or root page. This component handles the list of books. Open and edit `src/App.js` then replace all codes with this codes.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom';
import axios from 'axios';

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      books: []
    };
  }

  componentDidMount() {
    axios.get('/api/book')
      .then(res => {
        this.setState({ books: res.data });
        console.log(this.state.books);
      });
  }

  render() {
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              BOOK CATALOG
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/create"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> Add Book</Link></h4>
            <table class="table table-stripe">
              <thead>
                <tr>
                  <th>ISBN</th>
                  <th>Title</th>
                  <th>Author</th>
                </tr>
              </thead>
              <tbody>
                {this.state.books.map(book =>
                  <tr>
                    <td><Link to={`/show/${book._id}`}>{book.isbn}</Link></td>
                    <td>{book.title}</td>
                    <td>{book.author}</td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    );
  }
}

export default App;


8. MERN Stack: Add Create Components for Add New Book

For add a new book, open and edit `src/components/Create.js` then replace all codes with this codes.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import { Link } from 'react-router-dom';

class Create extends Component {

  constructor() {
    super();
    this.state = {
      isbn: '',
      title: '',
      author: '',
      description: '',
      published_year: '',
      publisher: ''
    };
  }
  onChange = (e) => {
    const state = this.state
    state[e.target.name] = e.target.value;
    this.setState(state);
  }

  onSubmit = (e) => {
    e.preventDefault();

    const { isbn, title, author, description, published_year, publisher } = this.state;

    axios.post('/api/book', { isbn, title, author, description, published_year, publisher })
      .then((result) => {
        this.props.history.push("/")
      });
  }

  render() {
    const { isbn, title, author, description, published_year, publisher } = this.state;
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              ADD BOOK
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Book List</Link></h4>
            <form onSubmit={this.onSubmit}>
              <div class="form-group">
                <label for="isbn">ISBN:</label>
                <input type="text" class="form-control" name="isbn" value={isbn} onChange={this.onChange} placeholder="ISBN" />
              </div>
              <div class="form-group">
                <label for="title">Title:</label>
                <input type="text" class="form-control" name="title" value={title} onChange={this.onChange} placeholder="Title" />
              </div>
              <div class="form-group">
                <label for="author">Author:</label>
                <input type="text" class="form-control" name="author" value={author} onChange={this.onChange} placeholder="Author" />
              </div>
              <div class="form-group">
                <label for="description">Description:</label>
                <textArea class="form-control" name="description" onChange={this.onChange} placeholder="Description" cols="80" rows="3">{description}</textArea>
              </div>
              <div class="form-group">
                <label for="published_date">Published Date:</label>
                <input type="number" class="form-control" name="published_year" value={published_year} onChange={this.onChange} placeholder="Published Year" />
              </div>
              <div class="form-group">
                <label for="publisher">Publisher:</label>
                <input type="text" class="form-control" name="publisher" value={publisher} onChange={this.onChange} placeholder="Publisher" />
              </div>
              <button type="submit" class="btn btn-default">Submit</button>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

export default Create;


9. MERN Stack: Add Show Component for Show Book Details

To show book details that listed on the home page, open and edit `src/components/Show.js` then add this lines of codes.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';

class Show extends Component {

  constructor(props) {
    super(props);
    this.state = {
      book: {}
    };
  }

  componentDidMount() {
    axios.get('/api/book/'+this.props.match.params.id)
      .then(res => {
        this.setState({ book: res.data });
        console.log(this.state.book);
      });
  }

  delete(id){
    console.log(id);
    axios.delete('/api/book/'+id)
      .then((result) => {
        this.props.history.push("/")
      });
  }

  render() {
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              {this.state.book.title}
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Book List</Link></h4>
            <dl>
              <dt>ISBN:</dt>
              <dd>{this.state.book.isbn}</dd>
              <dt>Author:</dt>
              <dd>{this.state.book.author}</dd>
              <dt>Description:</dt>
              <dd>{this.state.book.description}</dd>
              <dt>Publish Date:</dt>
              <dd>{this.state.book.published_year}</dd>
              <dt>Publisher:</dt>
              <dd>{this.state.book.publisher}</dd>
            </dl>
            <Link to={`/edit/${this.state.book._id}`} class="btn btn-success">Edit</Link>&nbsp;
            <button onClick={this.delete.bind(this, this.state.book._id)} class="btn btn-danger">Delete</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Show;
In this component, there is two button for edit current book and for delete current book. Delete function included in this component.


10. MERN Stack: Add Edit Component for Edit a Book

After the show book details, we need to edit the book. For that open and edit, `src/components/Edit.js` then add this lines of codes.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';

class Edit extends Component {

  constructor(props) {
    super(props);
    this.state = {
      book: {}
    };
  }

  componentDidMount() {
    axios.get('/api/book/'+this.props.match.params.id)
      .then(res => {
        this.setState({ book: res.data });
        console.log(this.state.book);
      });
  }

  onChange = (e) => {
    const state = this.state.book
    state[e.target.name] = e.target.value;
    this.setState({book:state});
  }

  onSubmit = (e) => {
    e.preventDefault();

    const { isbn, title, author, description, published_year, publisher } = this.state.book;

    axios.put('/api/book/'+this.props.match.params.id, { isbn, title, author, description, published_year, publisher })
      .then((result) => {
        this.props.history.push("/show/"+this.props.match.params.id)
      });
  }

  render() {
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              EDIT BOOK
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to={`/show/${this.state.book._id}`}><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> Book List</Link></h4>
            <form onSubmit={this.onSubmit}>
              <div class="form-group">
                <label for="isbn">ISBN:</label>
                <input type="text" class="form-control" name="isbn" value={this.state.book.isbn} onChange={this.onChange} placeholder="ISBN" />
              </div>
              <div class="form-group">
                <label for="title">Title:</label>
                <input type="text" class="form-control" name="title" value={this.state.book.title} onChange={this.onChange} placeholder="Title" />
              </div>
              <div class="form-group">
                <label for="author">Author:</label>
                <input type="text" class="form-control" name="author" value={this.state.book.author} onChange={this.onChange} placeholder="Author" />
              </div>
              <div class="form-group">
                <label for="description">Description:</label>
                <input type="text" class="form-control" name="description" value={this.state.book.description} onChange={this.onChange} placeholder="Description" />
              </div>
              <div class="form-group">
                <label for="published_date">Published Date:</label>
                <input type="number" class="form-control" name="published_year" value={this.state.book.published_year} onChange={this.onChange} placeholder="Published Year" />
              </div>
              <div class="form-group">
                <label for="publisher">Publisher:</label>
                <input type="text" class="form-control" name="publisher" value={this.state.book.publisher} onChange={this.onChange} placeholder="Publisher" />
              </div>
              <button type="submit" class="btn btn-default">Submit</button>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

export default Edit;


11. MERN Stack: Run the MERN App

Now, we can test our completed app by type this command.
npm run build
npm start
On the browser go to `localhost:3000` then you will see this home page.
Building CRUD Web Application using MERN Stack - MERN Application Home
You can navigate and populate the books data.

Vào XXX làm đi Bi, nhiều thứ hay ho mà?


Bi (con gái, sinh năm 1989, em của Kevin, đang làm cho công ty NTT Data tại Nhật): Hihi Bi cung thấy FPT hay
Nhưng mà Bi ko biết có những position gì ở trong đấy Bi thích hợp nhỉ. Cơ bản Bi thấy Bi không giỏi coding hay chuyên về kỹ thuật vì mấy năm nay ở NTT Data Bi ko làm cái đó. Bi chả biết 業務 mấy hehe, tuy nhiên Bi đọc 仕様書 thì nhanh. Mấy cái skills mềm như tiếng Anh, tiếng Nhật, presentation có logic hay đi thả thính thì Bi ok là mình làm đc, hoặc những việc làm cái mới thì ok, còn 定型作業 Bi ko thích.
Bi cũng thấy cty Bi chỉ cần một chú proper super BA (スーパー有識者) thôi chuyên về business chịu trách nhiệm một system 50 triệu usd, chuyên đi contact và lắng nghe khách hàng, chú cũng cực nắm system mọi chân tơ kẽ tóc nên được khách tin tưởng vô đối. Để thành super 有識者 cũng phải mất 10 năm hơn nắm nghiệp vụ của KH đó. Hiểu thứ khách hàng làm còn hơn là khách hàng nữa.
Một system mà chỉ cần 1 nhân viên NTT Data nắm nghiệp vụ, chừng 3 thằng infra cũng là NTT Data, còn lại là thuê ngoài hết, mà system $50M (bao gồm application lúc đầu và 5 năm bảo hành apps) thì Bi thấy hiệu suất trên đầu người cao ghê. Bi thấy đi theo hướng vertical như vậy là cực cần thiết thay vì offshore một đống người hàng ngang.
Để làm như vậy Bi thấy cty Bi thường hay pick up ra một anh sáng tài, cho anh ấy nắm nghiệp vụ của khách hàng hai ba năm, xong nuôi nó vài năm nữa gọi là super BA, tạo tin tưởng với khách hàng, sau nay họ ho cái gì là chạy lại gãi ngứa, gãi quen khách hàng cũng thích chỉ chọn mình.
Và do mình quá nắm nghiệp vụ như chân tơ kẽ tóc rồi nên khi mình viết 調達仕様書 cho những lần 業務改善 sau mình sẽ toàn viết những 調達事項 mà chỉ mình làm được, không thằng nào làm nổi do không quen, thế là KH cũng chỉ có thể chọn mình ko chọn ai, bài cty Bi cũng đơn giản chỉ có nhiêu đó.
Và đặc biệt là cty Bi đãi ngộ khá tốt so với các cty Nhật khác nên người vào ko hay bỏ ra làm cty khác, 1-5 năm đầu chỉ 育成 đến tầm năm thứ 6 tức giai đoạn của các anh 30t đến 40t thì cực giỏi, các sếp bukachou có 1 dàn cái tầm ba mươi mấy cực kì được việc nên phó mặc hết cho họ, bukachou chả làm gì cả, chỉ ăn rồi đi xin lỗi nếu xảy ra hoạ, tầm 40 là chả thấy development nữa, chỉ ngồi tính tiền thôi, trẻ khoẻ tầm 35 là cho làm bán sống bán chết.
Mấy cái kĩ thuật khó thì toàn nhờ vendor ngoài kentou xong đi troll mấy cty đó thôi à. Còn lại nhân viên NTT Data cho vào học nghiệp vụ và quản lý + lắng nghe khách hàng hết (hội delivery đó). Riêng hội infra tức là hội về kỹ thuật network thì giỏi IT thật.
Khi Bi đi làm với cty VN Bi thấy đa phần toàn thích code thay vì thích tìm hiểu sâu về nghiệp vụ để lần sau sáng chế ra thêm function mới. Thích nghe phía trên chỉ đâu làm đấy thay vì từng cá nhân tự create ra giá trị mới. Và ít thích tương tác tiếng Nhật (chắc do cty NTT Data ở Vietnam còn hạn chế tiếng Nhật và nhân viên còn non). Người VN có thế mạnh là thích ứng nhanh, không bị gò bó bởi những định kiến rườm rà tủn mủn của người Nhật, còn ngược lại người Nhật thì kĩ quá mức, cẩn thận như bà cụ già nên làm ra thành quả ít sai. Bi muốn chiết xuất được cả hai mặt tốt của cả hai bên để áp dụng vào công việc tương lai của mình.
Nói về khoản kỹ của Nhật, thằng senpai của Bi in tài liệu nó đóng ghim ở một vị trí rất lạ trước khi giao tài liệu cho khách hàng, Bi hỏi "ủa sao đóng vị trí lạ vậy?", nó bảo: vì để ý khách hàng hay kẹp bằng cái file này, nếu mình đóng như vầy khi họ dùng cái file của họ họ sẽ dễ tìm hơn. Ôi Bi mới học được hoá ra bọn Nhật nó kỹ kinh dị, đến cả việc đóng ghim trang giấy và quan sát khách như cú vọ để ko bị mắc cái lỗi nào hehe.
Mới thấy là để kiếm ra được đồng tiền hơn người, tốt nhất là từng nhân viên đều phải như gắn ăng-ten lên não vậy, ăng-ten là phải quan sát bốn phương tám hướng, đừng sa đà tám gẫu lảm nhảm, để rồi khách hàng sẽ chỉ tin mình ta. Ta mạnh rồi thì ta sẽ đi troll đi sai thằng khác làm những việc tốn thể lực hơn. Vậy thôi à. Vậy thôi chứ Bi cũng có 開発 mấy đâu.
Bên Bi là bên 事業部 tức là bên các project, còn có một bộ phận chuyên nghiên cứu IT, kỹ thuật, process chung cho toàn cty, bên đó gọi là 技術開発本部. Bên đó thì chỉ toàn tuyển những bạn cực giỏi IT, ăn rồi chỉ ngồi nghĩ quy trình chuẩn, và các framework xịn. Nghĩ ra xong output của bên đó sẽ được áp dụng vào các 事業部 (tức là các project mà có khách hàng bên ngoài). Đám 技術開発本部 nó không có khách hàng bên ngoài, chỉ ngồi nghĩ quy trình, nhưng 単価 của bọn nó cực cao. Một bạn của bên đó sẽ có 単価 cao gấp đôi một bạn bên 事業部 như Bi chẳng hạn.
Mô hình của cty Bi thì vendor ngoài bên dưới nó biết hết cả mà. Nó còn ngóng tin hành lang nhân sự bên Bi nhanh như điện, ngóng để biết bên Bi thay đổi nhân sự như thế nào rồi mà biết o bế ai cho nó hiệu quả, lại được thêm project.
Cuối cùng thật ra cty Bi chỉ có là cái brand, lòng tin KH, và khả năng + process quản lý guồng máy lớn thôi. Chứ thật ra IT technology thì giờ ai cũng làm đc, code gì ai chả code đc. Thành thử ra nó chả chăm chăm đi tuyển bọn giỏi code vào làm bao giờ, tiêu chí tuyển người của cty Bi là: sáng dạ để hấp thu nhanh, tư duy ngôn ngữ giỏi để comunication giữa người này với người kia đạt hiệu quả, thành thật ko ba liếm ba láp, và chăm chỉ. Thế là được nhận. Còn giỏi code hay ko này kia, biết nhiều hay ko ngày kia vô người train được hết. Thành ra cty Bi tuyển đủ thứ xuất thân: luật, kinh tế, xã hội học, vv...tuyển tất. Cứ em nào có potential là loại sáng dạ hấp thụ nhanh là nhận..
Bi đi làm thấy rằng tư duy ngôn ngữ nó cực kỳ quan trọng. Vô cùng luôn. Chị Mai cố gắng đào tạo cho 2 đứa có một passion với ngôn ngữ, nó sẽ là vũ khí cho mọi trường hợp sau này. Bởi vì communication là siêu cần thiết bây giờ. Tư duy ngôn ngữ tốt thì thiết kế cái gì nó cũng hợp lý, giải thích cho ai đó cũng nhanh và đủ ý, không vấp lỗi. Đi chém gió thì khách hàng nghe lọt lỗ tai, vẽ hình diễn đạt trôi chảy. Nói tóm lại là lợi trên mọi mặt. Chính vì vậy phương Tây nó cực kì coi trọng môn văn học lịch sử nghệ thuật, suy cho cùng cũng là dùng một hình thức nào đó (ngôn ngữ, hình ảnh,..) để diễn đạt điều mình cảm nhận và muốn truyền tải cho người khác. Napoleon đó, ngày còn bé toàn ngồi trong gác xép đọc hết sách này đến sách khác. Hoá ra vĩ nhân toàn thích đọc sách từ bé. Giờ thấy trẻ em VN toàn bấm facebook chụp ảnh tự sướng tán dóc vớ vẩn, đến là ngán ngẩm. Nhìn người Do Thái dạy con á, toàn dạy ra thiên tài bằng cách cho nó đọc sách từ bé xíu, vậy mới đẻ ra Einstein hehe. Mình ko cần con mình là thiên tài nhưng là một đứa trẻ có khả năng cảm thụ tốt EQ cao thì ko gì bằng...

@Bài Viết của Kevin

The Ultimate XP Project

  (Bài chia sẻ của tác giả  Ryo Amano ) Trong  bài viết  số này, tôi muốn viết về dự án phát triển phần mềm có áp dụng nguyên tắc phát triển...