remove existing radar and decouple everything
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,4 +6,4 @@ npm-debug.log
|
||||
yarn-error.log
|
||||
aoe_technology_radar.iml
|
||||
build
|
||||
bin
|
||||
# bin
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
A static site generator for AOE Technology Radar
|
||||
|
||||
## Usage for your own radar?
|
||||
|
||||
The generator is free to use under Open Source License - in fact there are already some other Radars published based on our Radar and there are also Contributions back.
|
||||
(There is a list of planned features below in case someone wants to contribute :-)
|
||||
|
||||
However please be aware:
|
||||
* The text and descriptions for the articles in the "radar" are copyright protected by AOE and they are not allowed to use in your radar
|
||||
* It would be nice to mention in radar that the generator is based on this repository.
|
||||
* Also when you want to reuse the CSS and Styling: Change the font (it is a licensed font) and the colors (It using AOE CI)
|
||||
|
||||
|
||||
40
bin/src/config.js
Normal file
40
bin/src/config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isMobileViewport = exports.translate = exports.getItemPageNames = exports.assetUrl = exports.rings = exports.quadrants = exports.radarNameShort = exports.radarName = void 0;
|
||||
exports.radarName = 'AOE Technology Radar';
|
||||
exports.radarNameShort = 'Technology Radar';
|
||||
exports.quadrants = [
|
||||
'languages-and-frameworks',
|
||||
'methods-and-patterns',
|
||||
'platforms-and-aoe-services',
|
||||
'tools',
|
||||
];
|
||||
exports.rings = [
|
||||
'all',
|
||||
'adopt',
|
||||
'trial',
|
||||
'assess',
|
||||
'hold'
|
||||
];
|
||||
// todo: fix
|
||||
function assetUrl(file) {
|
||||
return '/' + file;
|
||||
// return `/techradar/assets/${file}`
|
||||
}
|
||||
exports.assetUrl = assetUrl;
|
||||
exports.getItemPageNames = (items) => items.map(item => `${item.quadrant}/${item.name}`);
|
||||
const messages = {
|
||||
'languages-and-frameworks': 'Languages & Frameworks',
|
||||
'methods-and-patterns': 'Methods & Patterns',
|
||||
'platforms-and-aoe-services': 'Platforms and Operations',
|
||||
'tools': 'Tools',
|
||||
};
|
||||
exports.translate = (key) => (messages[key] || '-');
|
||||
function isMobileViewport() {
|
||||
// return false for server side rendering
|
||||
if (typeof window == 'undefined')
|
||||
return false;
|
||||
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
return width < 1200;
|
||||
}
|
||||
exports.isMobileViewport = isMobileViewport;
|
||||
18
bin/src/model.js
Normal file
18
bin/src/model.js
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.featuredOnly = void 0;
|
||||
exports.featuredOnly = (items) => items.filter(item => item.featured);
|
||||
exports.groupByQuadrants = (items) => items.reduce((quadrants, item) => (Object.assign(Object.assign({}, quadrants), { [item.quadrant]: addItemToQuadrant(quadrants[item.quadrant], item) })), {});
|
||||
exports.groupByFirstLetter = (items) => {
|
||||
const index = items.reduce((letterIndex, item) => (Object.assign(Object.assign({}, letterIndex), { [exports.getFirstLetter(item)]: addItemToList(letterIndex[exports.getFirstLetter(item)], item) })), {});
|
||||
return Object.keys(index)
|
||||
.sort()
|
||||
.map(letter => ({
|
||||
letter,
|
||||
items: index[letter],
|
||||
}));
|
||||
};
|
||||
const addItemToQuadrant = (quadrant = {}, item) => (Object.assign(Object.assign({}, quadrant), { [item.ring]: addItemToRing(quadrant[item.ring], item) }));
|
||||
const addItemToList = (list = [], item) => [...list, item];
|
||||
const addItemToRing = (ring = [], item) => [...ring, item];
|
||||
exports.getFirstLetter = (item) => item.title.substr(0, 1).toUpperCase();
|
||||
43
bin/tasks/file.js
Executable file
43
bin/tasks/file.js
Executable file
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.save = exports.getAllMarkdownFiles = exports.distPath = exports.jsPath = exports.faviconPath = exports.assetsPath = exports.stylesPath = exports.radarPath = exports.relativePath = void 0;
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const walk_1 = require("walk");
|
||||
exports.relativePath = (...relativePath) => (
|
||||
// path.resolve(__dirname, '..', ...relativePath)
|
||||
path_1.default.resolve(...relativePath));
|
||||
exports.radarPath = (...pathInSrc) => (exports.relativePath('radar', ...pathInSrc));
|
||||
exports.stylesPath = (...pathInSrc) => (exports.relativePath('styles', ...pathInSrc));
|
||||
exports.assetsPath = (...pathInSrc) => (exports.relativePath('assets', ...pathInSrc));
|
||||
exports.faviconPath = (...pathInSrc) => (exports.relativePath('assets/favicon.ico', ...pathInSrc));
|
||||
exports.jsPath = (...pathInSrc) => (exports.relativePath('js', ...pathInSrc));
|
||||
exports.distPath = (...pathInDist) => (exports.relativePath('src', ...pathInDist));
|
||||
exports.getAllMarkdownFiles = (folder) => (getAllFiles(folder, isMarkdownFile));
|
||||
const getAllFiles = (folder, predicate) => (new Promise((resolve, reject) => {
|
||||
const walker = walk_1.walk(folder, { followLinks: false });
|
||||
const files = [];
|
||||
walker.on("file", (root, fileStat, next) => {
|
||||
if (predicate(fileStat.name)) {
|
||||
files.push(path_1.default.resolve(root, fileStat.name));
|
||||
}
|
||||
next();
|
||||
});
|
||||
walker.on("errors", (root, nodeStatsArray, next) => {
|
||||
nodeStatsArray.forEach(function (n) {
|
||||
console.error("[ERROR] " + n.name);
|
||||
if (n.error) {
|
||||
console.error(n.error.message || (n.error.code + ": " + n.error.path));
|
||||
}
|
||||
});
|
||||
next();
|
||||
});
|
||||
walker.on("end", () => {
|
||||
resolve(files.sort());
|
||||
});
|
||||
}));
|
||||
const isMarkdownFile = (name) => name.match(/\.md$/) !== null;
|
||||
exports.save = (data, fileName) => fs_extra_1.outputFile(exports.distPath(fileName), data);
|
||||
128
bin/tasks/radar.js
Executable file
128
bin/tasks/radar.js
Executable file
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createRadar = void 0;
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const front_matter_1 = __importDefault(require("front-matter"));
|
||||
const marked_1 = __importDefault(require("marked"));
|
||||
const highlight_js_1 = __importDefault(require("highlight.js"));
|
||||
const config_1 = require("../src/config");
|
||||
const file_1 = require("./file");
|
||||
marked_1.default.setOptions({
|
||||
highlight: code => highlight_js_1.default.highlightAuto(code).value,
|
||||
});
|
||||
exports.createRadar = () => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const fileNames = yield file_1.getAllMarkdownFiles(file_1.radarPath());
|
||||
const revisions = yield createRevisionsFromFiles(fileNames);
|
||||
const allReleases = getAllReleases(revisions);
|
||||
const items = createItems(revisions);
|
||||
const flaggedItems = flagItem(items, allReleases);
|
||||
return {
|
||||
items: flaggedItems,
|
||||
releases: allReleases,
|
||||
};
|
||||
});
|
||||
const checkAttributes = (fileName, attributes) => {
|
||||
if (attributes.ring && !config_1.rings.includes(attributes.ring)) {
|
||||
throw new Error(`Error: ${fileName} has an illegal value for 'ring' - must be one of ${config_1.rings}`);
|
||||
}
|
||||
if (attributes.quadrant && !config_1.quadrants.includes(attributes.quadrant)) {
|
||||
throw new Error(`Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${config_1.quadrants}`);
|
||||
}
|
||||
if (!attributes.quadrant || attributes.quadrant === '') {
|
||||
// throw new Error(`Error: ${fileName} has no 'quadrant' set`);
|
||||
}
|
||||
if (!attributes.title || attributes.title === '') {
|
||||
attributes.title = path_1.default.basename(fileName);
|
||||
}
|
||||
return attributes;
|
||||
};
|
||||
const createRevisionsFromFiles = (fileNames) => Promise.all(fileNames.map(fileName => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs_extra_1.readFile(fileName, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
const fm = front_matter_1.default(data);
|
||||
// add target attribute to external links
|
||||
// todo: check path
|
||||
let html = marked_1.default(fm.body.replace(/\]\(\//g, '](/techradar/'));
|
||||
html = html.replace(/a href="http/g, 'a target="_blank" rel="noopener noreferrer" href="http');
|
||||
resolve(Object.assign(Object.assign(Object.assign({}, itemInfoFromFilename(fileName)), checkAttributes(fileName, fm.attributes)), { fileName, body: html }));
|
||||
}
|
||||
});
|
||||
});
|
||||
}));
|
||||
const itemInfoFromFilename = (fileName) => {
|
||||
const [release, nameWithSuffix] = fileName.split(path_1.default.sep).slice(-2);
|
||||
return {
|
||||
name: nameWithSuffix.substr(0, nameWithSuffix.length - 3),
|
||||
release,
|
||||
};
|
||||
};
|
||||
const getAllReleases = (revisions) => revisions
|
||||
.reduce((allReleases, { release }) => {
|
||||
if (!allReleases.includes(release)) {
|
||||
return [...allReleases, release];
|
||||
}
|
||||
return allReleases;
|
||||
}, [])
|
||||
.sort();
|
||||
const createItems = (revisions) => {
|
||||
const itemMap = revisions.reduce((items, revision) => {
|
||||
return Object.assign(Object.assign({}, items), { [revision.name]: addRevisionToItem(items[revision.name], revision) });
|
||||
}, {});
|
||||
return Object.values(itemMap).sort((x, y) => (x.name > y.name ? 1 : -1));
|
||||
};
|
||||
const ignoreEmptyRevisionBody = (revision, item) => {
|
||||
if (!revision.body || revision.body.trim() === '') {
|
||||
return item.body;
|
||||
}
|
||||
return revision.body;
|
||||
};
|
||||
const addRevisionToItem = (item = {
|
||||
flag: 'default',
|
||||
featured: true,
|
||||
revisions: [],
|
||||
name: '',
|
||||
title: '',
|
||||
ring: 'trial',
|
||||
quadrant: '',
|
||||
body: '',
|
||||
info: '',
|
||||
}, revision) => {
|
||||
let newItem = Object.assign(Object.assign(Object.assign({}, item), revision), { body: ignoreEmptyRevisionBody(revision, item) });
|
||||
if (revisionCreatesNewHistoryEntry(revision)) {
|
||||
newItem = Object.assign(Object.assign({}, newItem), { revisions: [revision, ...newItem.revisions] });
|
||||
}
|
||||
return newItem;
|
||||
};
|
||||
const revisionCreatesNewHistoryEntry = (revision) => {
|
||||
return revision.body.trim() !== '' || typeof revision.ring !== 'undefined';
|
||||
};
|
||||
const flagItem = (items, allReleases) => items.map(item => (Object.assign(Object.assign({}, item), { flag: getItemFlag(item, allReleases) })), []);
|
||||
const isInLastRelease = (item, allReleases) => item.revisions[0].release === allReleases[allReleases.length - 1];
|
||||
const isNewItem = (item, allReleases) => item.revisions.length === 1 && isInLastRelease(item, allReleases);
|
||||
const hasItemChanged = (item, allReleases) => item.revisions.length > 1 && isInLastRelease(item, allReleases);
|
||||
const getItemFlag = (item, allReleases) => {
|
||||
if (isNewItem(item, allReleases)) {
|
||||
return 'new';
|
||||
}
|
||||
if (hasItemChanged(item, allReleases)) {
|
||||
return 'changed';
|
||||
}
|
||||
return 'default';
|
||||
};
|
||||
45
bin/tasks/radarjson.js
Executable file
45
bin/tasks/radarjson.js
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const radar_1 = require("./radar");
|
||||
const file_1 = require("./file");
|
||||
(() => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
console.log('start');
|
||||
const radar = yield radar_1.createRadar();
|
||||
// console.log(radar);
|
||||
file_1.save(JSON.stringify(radar), 'rd.json');
|
||||
file_1.save(`import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from 'aoe_technology_radar/src/components/App';
|
||||
import 'aoe_technology_radar/src/index.scss';
|
||||
import {Item} from "aoe_technology_radar/src/model";
|
||||
import radardata from './rd.json';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App items={radardata.items as Item[]} releases={radardata.releases as string[]} />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
`, 'index.tsx');
|
||||
// getPageNames(radar).map(pageName => {
|
||||
// // const pageHtml = renderPage(radar, pageName);
|
||||
// // save(pageHtml, `${pageName}.html`);
|
||||
// save([pageName, radar], `${pageName}.html`)
|
||||
// });
|
||||
console.log('Built radar');
|
||||
}
|
||||
catch (e) {
|
||||
console.error('error:', e);
|
||||
}
|
||||
}))();
|
||||
101
config/env.js
Normal file
101
config/env.js
Normal file
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
const dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
// We support configuring the sockjs pathname during development.
|
||||
// These settings let a developer run multiple simultaneous projects.
|
||||
// They are used as the connection `hostname`, `pathname` and `port`
|
||||
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
|
||||
// and `sockPort` options in webpack-dev-server.
|
||||
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
|
||||
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
|
||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
||||
66
config/getHttpsConfig.js
Normal file
66
config/getHttpsConfig.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Ensure the certificate and key provided are valid and if not
|
||||
// throw an easy to debug error
|
||||
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
||||
let encrypted;
|
||||
try {
|
||||
// publicEncrypt will throw an error with an invalid cert
|
||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// privateDecrypt will throw an error with an invalid key
|
||||
crypto.privateDecrypt(key, encrypted);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read file and throw an error if it doesn't exist
|
||||
function readEnvFile(file, type) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(
|
||||
`You specified ${chalk.cyan(
|
||||
type
|
||||
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
|
||||
);
|
||||
}
|
||||
return fs.readFileSync(file);
|
||||
}
|
||||
|
||||
// Get the https config
|
||||
// Return cert files if provided in env, otherwise just true or false
|
||||
function getHttpsConfig() {
|
||||
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
||||
const isHttps = HTTPS === 'true';
|
||||
|
||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||
const config = {
|
||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
||||
};
|
||||
|
||||
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
||||
return config;
|
||||
}
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
module.exports = getHttpsConfig;
|
||||
14
config/jest/cssTransform.js
Normal file
14
config/jest/cssTransform.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
},
|
||||
getCacheKey() {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
},
|
||||
};
|
||||
40
config/jest/fileTransform.js
Normal file
40
config/jest/fileTransform.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const camelcase = require('camelcase');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
const assetFilename = JSON.stringify(path.basename(filename));
|
||||
|
||||
if (filename.match(/\.svg$/)) {
|
||||
// Based on how SVGR generates a component name:
|
||||
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
|
||||
const pascalCaseFilename = camelcase(path.parse(filename).name, {
|
||||
pascalCase: true,
|
||||
});
|
||||
const componentName = `Svg${pascalCaseFilename}`;
|
||||
return `const React = require('react');
|
||||
module.exports = {
|
||||
__esModule: true,
|
||||
default: ${assetFilename},
|
||||
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
type: 'svg',
|
||||
ref: ref,
|
||||
key: null,
|
||||
props: Object.assign({}, props, {
|
||||
children: ${assetFilename}
|
||||
})
|
||||
};
|
||||
}),
|
||||
};`;
|
||||
}
|
||||
|
||||
return `module.exports = ${assetFilename};`;
|
||||
},
|
||||
};
|
||||
141
config/modules.js
Normal file
141
config/modules.js
Normal file
@@ -0,0 +1,141 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const resolve = require('resolve');
|
||||
|
||||
/**
|
||||
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
// We need to explicitly check for null and undefined (and not a falsy value) because
|
||||
// TypeScript treats an empty string as `.`.
|
||||
if (baseUrl == null) {
|
||||
// If there's no baseUrl set we respect NODE_PATH
|
||||
// Note that NODE_PATH is deprecated and will be removed
|
||||
// in the next major release of create-react-app.
|
||||
|
||||
const nodePath = process.env.NODE_PATH || '';
|
||||
return nodePath.split(path.delimiter).filter(Boolean);
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||
// the default behavior.
|
||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow the user set the `baseUrl` to `appSrc`.
|
||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
||||
return [paths.appSrc];
|
||||
}
|
||||
|
||||
// If the path is equal to the root directory we ignore it here.
|
||||
// We don't want to allow importing from the root directly as source files are
|
||||
// not transpiled outside of `src`. We do allow importing them with the
|
||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||
// an alias.
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, throw an error.
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get webpack aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getWebpackAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
src: paths.appSrc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get jest aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getJestAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getModules() {
|
||||
// Check if TypeScript is setup
|
||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||
const hasJsConfig = fs.existsSync(paths.appJsConfig);
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
);
|
||||
}
|
||||
|
||||
let config;
|
||||
|
||||
// If there's a tsconfig.json we assume it's a
|
||||
// TypeScript project and set up the config
|
||||
// based on tsconfig.json
|
||||
if (hasTsConfig) {
|
||||
const ts = require(resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}));
|
||||
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
||||
// Otherwise we'll check if there is jsconfig.json
|
||||
// for non TS projects.
|
||||
} else if (hasJsConfig) {
|
||||
config = require(paths.appJsConfig);
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
const options = config.compilerOptions || {};
|
||||
|
||||
const additionalModulePaths = getAdditionalModulePaths(options);
|
||||
|
||||
return {
|
||||
additionalModulePaths: additionalModulePaths,
|
||||
webpackAliases: getWebpackAliases(options),
|
||||
jestAliases: getJestAliases(options),
|
||||
hasTsConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getModules();
|
||||
75
config/paths.js
Normal file
75
config/paths.js
Normal file
@@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
const templateDirectory = fs.realpathSync(__dirname);
|
||||
const resolveTemplate = relativePath => path.resolve(templateDirectory, '..', relativePath);
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const publicUrlOrPath = getPublicUrlOrPath(
|
||||
process.env.NODE_ENV === 'development',
|
||||
require(resolveApp('package.json')).homepage,
|
||||
process.env.PUBLIC_URL
|
||||
);
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appPath: resolveApp('.'),
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveTemplate('public'),
|
||||
appHtml: resolveTemplate('public/index.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
templateSrc: resolveTemplate('src'),
|
||||
appTsConfig: resolveTemplate('tsconfig.json'),
|
||||
appJsConfig: resolveTemplate('jsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveTemplate('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrlOrPath,
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
35
config/pnpTs.js
Normal file
35
config/pnpTs.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const { resolveModuleName } = require('ts-pnp');
|
||||
|
||||
exports.resolveModuleName = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveModuleName
|
||||
);
|
||||
};
|
||||
|
||||
exports.resolveTypeReferenceDirective = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveTypeReferenceDirective
|
||||
);
|
||||
};
|
||||
670
config/webpack.config.js
Normal file
670
config/webpack.config.js
Normal file
@@ -0,0 +1,670 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const safePostCssParser = require('postcss-safe-parser');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
||||
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
|
||||
const appPackageJson = require(paths.appPackageJson);
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
|
||||
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
|
||||
|
||||
const imageInlineSizeLimit = parseInt(
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
|
||||
);
|
||||
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function(webpackEnv) {
|
||||
const isEnvDevelopment = webpackEnv === 'development';
|
||||
const isEnvProduction = webpackEnv === 'production';
|
||||
|
||||
// Variable used for enabling profiling in Production
|
||||
// passed into alias object. Uses a flag if passed into the build command
|
||||
const isEnvProductionProfile =
|
||||
isEnvProduction && process.argv.includes('--profile');
|
||||
|
||||
// We will provide `paths.publicUrlOrPath` to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||
// in production `paths.publicUrlOrPath` can be a relative path
|
||||
options: paths.publicUrlOrPath.startsWith('.')
|
||||
? { publicPath: '../../' }
|
||||
: {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
}),
|
||||
// Adds PostCSS Normalize as the reset css with default options,
|
||||
// so that it honors browserslist config in package.json
|
||||
// which in turn let's users customize the target behavior as per their needs.
|
||||
postcssNormalize(),
|
||||
],
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
].filter(Boolean);
|
||||
if (preProcessor) {
|
||||
loaders.push(
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
|
||||
return {
|
||||
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
|
||||
// Stop compilation early in production
|
||||
bail: isEnvProduction,
|
||||
devtool: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
? 'source-map'
|
||||
: false
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: isEnvDevelopment,
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// In development, it does not produce real files.
|
||||
filename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].js'
|
||||
: isEnvDevelopment && 'static/js/bundle.js',
|
||||
// TODO: remove this when upgrading to webpack 5
|
||||
futureEmitAssets: true,
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
// Prevents conflicts when multiple webpack runtimes (from different apps)
|
||||
// are used on the same page.
|
||||
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
// module chunks which are built will work in web workers as well.
|
||||
globalObject: 'this',
|
||||
},
|
||||
optimization: {
|
||||
minimize: isEnvProduction,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
// Added for profiling in devtools
|
||||
keep_classnames: isEnvProductionProfile,
|
||||
keep_fnames: isEnvProductionProfile,
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// This is only used in production mode
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
parser: safePostCssParser,
|
||||
map: shouldUseSourceMap
|
||||
? {
|
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false,
|
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true,
|
||||
}
|
||||
: false,
|
||||
},
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
name: false,
|
||||
},
|
||||
// Keep the runtime chunk separated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
runtimeChunk: {
|
||||
name: entrypoint => `runtime-${entrypoint.name}`,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
modules.additionalModulePaths || []
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: paths.moduleFileExtensions
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
// Allows for better profiling with ReactDevTools
|
||||
...(isEnvProductionProfile && {
|
||||
'react-dom$': 'react-dom/profiling',
|
||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
||||
}),
|
||||
...(modules.webpackAliases || {}),
|
||||
},
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
// {
|
||||
// test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
// enforce: 'pre',
|
||||
// use: [
|
||||
// {
|
||||
// options: {
|
||||
// cache: true,
|
||||
// formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
// eslintPath: require.resolve('eslint'),
|
||||
// resolvePluginsRelativeTo: __dirname,
|
||||
// },
|
||||
// loader: require.resolve('eslint-loader'),
|
||||
// },
|
||||
// ],
|
||||
// include: paths.appSrc,
|
||||
// },
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: [paths.appSrc, paths.templateSrc],
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides'
|
||||
),
|
||||
|
||||
presets:['react-app'],
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-named-asset-import'),
|
||||
{
|
||||
loaderMap: {
|
||||
svg: {
|
||||
ReactComponent:
|
||||
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{
|
||||
test: /\.(js|mjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app/dependencies'),
|
||||
{ helpers: true },
|
||||
],
|
||||
],
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'static/css/[name].[contenthash:8].css',
|
||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate an asset manifest file with the following content:
|
||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||
// output file so that tools can pick it up without having to parse
|
||||
// `index.html`
|
||||
// - "entrypoints" key: Array of files which are included in `index.html`,
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path;
|
||||
return manifest;
|
||||
}, seed);
|
||||
const entrypointFiles = entrypoints.main.filter(
|
||||
fileName => !fileName.endsWith('.map')
|
||||
);
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
};
|
||||
},
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the webpack build.
|
||||
isEnvProduction &&
|
||||
new WorkboxWebpackPlugin.GenerateSW({
|
||||
clientsClaim: true,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: paths.publicUrlOrPath + 'index.html',
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude any URLs whose last part seems to be a file extension
|
||||
// as they're likely a resource and not a SPA route.
|
||||
// URLs containing a "?" character won't be blacklisted as they're likely
|
||||
// a route with query params (e.g. auth callbacks).
|
||||
new RegExp('/[^/?]+\\.[^/]+$'),
|
||||
],
|
||||
}),
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: isEnvDevelopment,
|
||||
useTypescriptIncrementalApi: true,
|
||||
checkSyntacticErrors: true,
|
||||
resolveModuleNameModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
resolveTypeReferenceDirectiveModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
tsconfig: paths.appTsConfig,
|
||||
reportFiles: [
|
||||
'**',
|
||||
'!**/__tests__/**',
|
||||
'!**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
silent: true,
|
||||
// The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
dns: 'mock',
|
||||
fs: 'empty',
|
||||
http2: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
};
|
||||
};
|
||||
130
config/webpackDevServer.config.js
Normal file
130
config/webpackDevServer.config.js
Normal file
@@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
||||
const paths = require('./paths');
|
||||
const getHttpsConfig = require('./getHttpsConfig');
|
||||
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
|
||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebook/create-react-app/issues/2271
|
||||
// https://github.com/facebook/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
contentBasePublicPath: paths.publicUrlOrPath,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// Use 'ws' instead of 'sockjs-node' on server since we're using native
|
||||
// websockets in `webpackHotDevClient`.
|
||||
transportMode: 'ws',
|
||||
// Prevent a WS client from getting injected as we're already including
|
||||
// `webpackHotDevClient`.
|
||||
injectClient: false,
|
||||
// Enable custom sockjs pathname for websocket connection to hot reloading server.
|
||||
// Enable custom sockjs hostname, pathname and port for websocket connection
|
||||
// to hot reloading server.
|
||||
sockHost,
|
||||
sockPath,
|
||||
sockPort,
|
||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||
// we specified in the webpack config. When homepage is '.', default to serving
|
||||
// from the root.
|
||||
// remove last slash so user can land on `/test` instead of `/test/`
|
||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebook/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebook/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
https: getHttpsConfig(),
|
||||
host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
index: paths.publicUrlOrPath,
|
||||
},
|
||||
public: allowedHost,
|
||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||
proxy,
|
||||
before(app, server) {
|
||||
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
|
||||
// middlewares before `redirectServedPath` otherwise will not have any effect
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
},
|
||||
after(app) {
|
||||
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
|
||||
app.use(redirectServedPath(paths.publicUrlOrPath));
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||
},
|
||||
};
|
||||
};
|
||||
70
package.json
70
package.json
@@ -4,18 +4,11 @@
|
||||
"description": "AOE Technology Radar",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build_": "react-scripts build",
|
||||
"build": "tsc",
|
||||
"test": "react-scripts test",
|
||||
"_build": "yarn clean && yarn build:pages && yarn build:jsprod && yarn build:css && yarn build:assets",
|
||||
"build:all": "yarn build",
|
||||
"build:pages": "yarn run ts-node-script ./tasks/radarjson.ts",
|
||||
"build:css": "postcss -c postcss.config.js -o dist/techradar/assets/css/styles.css styles/main.css",
|
||||
"type-check": "tsc --noEmit"
|
||||
"build": "tsc"
|
||||
},
|
||||
"bin": {
|
||||
"create-radar": "bin/tasks/radarjson.js"
|
||||
"aoe-radar-create": "bin/tasks/radarjson.js",
|
||||
"aoe-radar-build": "scripts/build.js"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -32,6 +25,9 @@
|
||||
"author": "AOE GmbH <contact-de@aoe.com> (http://www.aoe.com)",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/plugin-syntax-jsx": "^7.12.1",
|
||||
"@svgr/webpack": "4.3.3",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
@@ -45,33 +41,81 @@
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/walk": "^2.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||
"@typescript-eslint/parser": "^2.10.0",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"babel-plugin-named-asset-import": "^0.3.6",
|
||||
"babel-preset-react-app": "^9.1.2",
|
||||
"camelcase": "^5.3.1",
|
||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||
"classnames": "^2.2.6",
|
||||
"css-loader": "3.4.2",
|
||||
"css-mqpacker": "^6.0.1",
|
||||
"csstype": "^2.6.11",
|
||||
"dotenv": "8.2.0",
|
||||
"dotenv-expand": "5.1.0",
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-config-react-app": "^5.2.1",
|
||||
"eslint-loader": "3.0.3",
|
||||
"eslint-plugin-flowtype": "4.6.0",
|
||||
"eslint-plugin-import": "2.20.1",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.19.0",
|
||||
"eslint-plugin-react-hooks": "^1.6.1",
|
||||
"file-loader": "4.3.0",
|
||||
"front-matter": "2.3.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"highlight.js": "9.12.0",
|
||||
"history": "4.7.2",
|
||||
"html-webpack-plugin": "4.0.0-beta.11",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"jest": "24.9.0",
|
||||
"jest-environment-jsdom-fourteen": "1.0.1",
|
||||
"jest-resolve": "24.9.0",
|
||||
"jest-watch-typeahead": "0.4.2",
|
||||
"live-server": "1.2.1",
|
||||
"marked": "0.3.18",
|
||||
"mini-css-extract-plugin": "0.9.0",
|
||||
"moment": "2.22.1",
|
||||
"node-sass": "^4.14.1",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||
"pnp-webpack-plugin": "1.6.4",
|
||||
"postcss-cli": "^7.1.1",
|
||||
"postcss-css-variables": "0.8.0",
|
||||
"postcss-custom-media": "^6.0.0",
|
||||
"postcss-easy-import": "3.0.0",
|
||||
"postcss-flexbugs-fixes": "4.1.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"postcss-nested": "2.1.2",
|
||||
"postcss-normalize": "8.0.1",
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "4.0.1",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-app-polyfill": "^1.0.6",
|
||||
"react-dev-utils": "^10.2.1",
|
||||
"react-dom": "16.12.0",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.1",
|
||||
"redux": "3.7.2",
|
||||
"resolve": "1.15.0",
|
||||
"resolve-url-loader": "3.1.1",
|
||||
"sass-loader": "8.0.2",
|
||||
"semver": "6.3.0",
|
||||
"style-loader": "0.23.1",
|
||||
"terser-webpack-plugin": "2.3.5",
|
||||
"ts-loader": "^8.0.1",
|
||||
"ts-node": "^8.10.2",
|
||||
"ts-pnp": "1.1.6",
|
||||
"typescript": "^3.9.6",
|
||||
"walk": "2.3.9"
|
||||
"url-loader": "2.3.0",
|
||||
"walk": "2.3.9",
|
||||
"webpack": "4.42.0",
|
||||
"webpack-dev-server": "3.10.3",
|
||||
"webpack-manifest-plugin": "2.2.0",
|
||||
"workbox-webpack-plugin": "4.3.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const postcssEasyImport = require('postcss-easy-import');
|
||||
const postcssNested = require('postcss-nested');
|
||||
const postcssCustomMedia = require('postcss-custom-media');
|
||||
const postcssCssVariables = require('postcss-css-variables');
|
||||
const postcssMqPacker = require('css-mqpacker');
|
||||
const postcssAutoprefixer = require('autoprefixer');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
postcssEasyImport(),
|
||||
postcssNested(),
|
||||
postcssCustomMedia(),
|
||||
postcssCssVariables(),
|
||||
postcssMqPacker(),
|
||||
postcssAutoprefixer({
|
||||
browsers: '> 5%',
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Akeneo"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
Akeneo is a Product Information Management system (also known as PIM, PCM or Product MDM) and helps centralize and harmonize all the technical and marketing information of products.
|
||||
|
||||
We use Akeneo with success in our projects and products (For example in OM3), where it is responsible for:
|
||||
|
||||
- Keeping product data separate from other applications - such as E-Commerce systems
|
||||
- Managing livecycles of products and managing product portfolios with their category structures
|
||||
- Managing attributes and families and therefore acting as attribute master for the suite
|
||||
|
||||
The system has a modern and friendly user interface and product managers find things such as completenesscheck, translation views and mass editing very helpful.
|
||||
|
||||
With delta export and import capabilities and the usage of Mongo DB as persitence backend, the performance is acceptable. We miss a richer API - but the system is extendable and based on PHP/Symfony 2.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Akka"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
With the growing adoption of microservice-based architecures, the interest in frameworks and tools that make building systems that follow the reactive manifesto possible has increased.
|
||||
|
||||
Akka provides you a toolkit and runtime based on the Actor model known from Erlang to reach this goal.
|
||||
|
||||
It's one of the most-adopted toolkits in its space with its key contributors beeing heavily involved in the overall movement of the reactive community as well.
|
||||
At AOE, we use Akka when we need high-performance, efficient data processing or where its finite state machine plays nicely with the domain of the application. It is worth mentioning that the actor model might come with extra complexity and therefore should be used in problem spaces where the advantages of this approach bring enough value and no accidental complexity.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "Angular"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
The latest version of the Angular Framework, which is used for large single-page applications.
|
||||
|
||||
[Angular](https://angular.io/) is a complete rewrite of Angular 1 — many things have changed compared to the first version. The latest best practices and toolings from the JavaScript community have found their way into Angular.
|
||||
|
||||
It supports DI (dependency injection), it has a clean inheritance and a good separation of concerns. Angular follows the [web component standards](https://www.w3.org/standards/techs/components#w3c_all) to avoid negative side effects between components.
|
||||
|
||||
We think that Angular is well-structured on both a development and an application level.
|
||||
|
||||
When talking about Angular, we must consider the [angular.cli](https://cli.angular.io/) as well, which provides a huge level of intelligent automation along the development process and project setup.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "Ant"
|
||||
ring: hold
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
Apache Ant was build in 1997 to have something like Make in the C/C++ world for Java. Ant uses xml files to describe steps required to produce executable artifacts from source code. The main concepts of tasks and targets are programmable in an imperative style.
|
||||
|
||||
Apache Ant was and is widely used by large software projects. Our recommendation is to stop using Apache Ant for new projects. If you are free to choose, we recommend Gradle as an Apache Ant replacement.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "Anypoint platform"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
Anypoint platform (formally known as Mule or Mule ESB) is an Enterprise Integration Platform written in Java.
|
||||
|
||||
Anypoint provide tools to use Enterprise Integration Patterns (EAI) and has a high number of ready-to-use connectors to communicate with software tools such as SAP, Salesforce, etc.
|
||||
|
||||
Anypoint Community Version is Open Source and contribution is possible. The platform is pluggable with own connectors. Mulesoft is also driving the [raml](/tools/raml.html) specification and related Open Source tools.
|
||||
|
||||
AOE is a Mulesoft Partner and we use both the Community and Enterprise Versions of Anypoint. We use Anypoint as an API Gateway to combine and transform data from multiple backends. We use it as ESB or Integration platform for loose coupling of software components. And we also use it as legacy modernization to provide modern APIs for legacy- or foreign software.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "API-First Design Approach"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
The API-First Design Approach puts the API design at the beginning of the implementation without any constraints, for example, from the current IT infrastructure or the implementation itself. The idea is to design the API in a way that it serves its purpose best and the consumers are enabled to work efficiently.
|
||||
|
||||
There are several advantages to this approach. For example, it can help to avoid reflecting the internal structure of the application or any internal constraints. Furthermore, as one of the most important design aspects is consistency, one can define features such as the behavior of security, URL schemes, and API keys upfront. It also helps speed up parallel implementation. A team that consumes the API can start working directly after the API design because it can easily be mocked.
|
||||
|
||||
There are several tools for modelling an API, but here at AOE we mainly use [RAML](/tools/raml.html) as it provides a rich set of tools for generating documentation, mocking and more. For mocking we use [Wiremock](/tools/wiremock.html), for example.
|
||||
|
||||
Related to the "API-First" approach is the "Headless" approach where an existing application (with or without existing API) is used as a backend for a separate frontend. We used this with sucess for Magento-based E-Commerce platforms. This allows encapsulating the core features of that application, while integrating it into a larger landscape of components using its API as a unified way to interact between components. Decoupling the core logic from its presentation layer allows picking the best technology stack for the various parts independently.
|
||||
|
||||
For further reading see:
|
||||
* [Understanding API First Design](https://www.programmableweb.com/api-university/understanding-api-first-design)
|
||||
* [When crafting your API strategy, put design first](http://www.techradar.com/news/software/applications/when-crafting-your-api-strategy-put-design-first-1262043?src=rss&attr=all)
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: "Artifactory"
|
||||
ring: trial
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
JFrog [Artifactory ](https://www.jfrog.com/open-source/)is a software tool, which, in the end, manages and stores (binary) artifacts.
|
||||
In addition to storage, it provides a managing interface, which also allows to store build information, properties as well as dependencies per artifact which are organized within repositories. A fine grained security system enables easy management of which artifacts are available to whom.
|
||||
The artifacts are exposed via an HTTP(S)-Url Artifactory, which can generate package-manager compatible manifests for the repositories. AOE utilizes Artifactory to serve Maven, Apt, Npm, Composer and Docker Repositories.
|
||||
|
||||
In addition to storing own assets, Artifactory is able to proxy remote Repository for and cache resolved artifacts locally.
|
||||
This results in an increased build performance and decouples builds from external service dependencies and ensures builds still work even if they utilize outdated dependencies that might not be publicly available anymore.
|
||||
|
||||
Artifactory provides a powerful REST-API for managing Artifacts including a powerful search AQL. It is utilized to provide complex release processes based on QA-Attributes on an artifact level.
|
||||
|
||||
Artifactory at AOE currently comes with some problems, too:
|
||||
* Cleanup in Artifactory has to be done manually. Therefore, if every build is pushed to Artifactory it currently pollutes disk space since old or unused versions are never removed.
|
||||
* The Composer Integration mirroring github proves to be slower than directly connecting to github.
|
||||
|
||||
AOE is using the Professional version for a central instance that can be used by different teams. We encourage teams to use Artifactory instead of Jenkins to store and manage build artifacts - and to take care of cleaning up old artifacts automatically.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "AWS Lambda"
|
||||
ring: trial
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
AWS Lambda is one of the exciting new "cloud-native" / serverless ways to run code without worrying about infrastructure. While it is possible to directly respond to web requests using the API Gateway, our teams are currently using AWS Lambda mostly for tasks outside the critical path. As a custom resource for CloudFormation, it allows us to manage all aspects of a deployment in an elegant way by simply deploying a new CloudFormation stack. Baking AMIs and doing green/blue switches are only two of the many use cases where AWS Lambda comes in very handy.
|
||||
|
||||
In addition to deployment automation, we're using AWS Lambda to process incoming data. Being able to respond to events from various sources such as S3 Buckets, SNS topics, Kinesis streams and HTTP endpoints it's a perfect match to process, transform and forward incoming data in near-realtime at a fraction of the cost of running an ESB.
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: "Babel"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
[Babel](https://babeljs.io/) gives you the possibility to use the latest features from JavaScript ([ECMAScript](https://en.wikipedia.org/wiki/ECMAScript)) in the browser of your choice.
|
||||
|
||||
Without Babel you had to use the feature set of your oldest browser or use feature detections such as [modernizr](https://modernizr.com/) or write polyfills on your own.
|
||||
|
||||
In general, Babel is split in 2 ways to bring you the new goodies you want.
|
||||
|
||||
1. New syntax will be compiled to old EcmaScript 5 code e.g.:
|
||||
|
||||
* [arrow-functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
|
||||
* [generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator)
|
||||
* [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
|
||||
* [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)
|
||||
* [...](https://babeljs.io/learn-es2015/)
|
||||
|
||||
2. New globals and functions are provided by [babel-polyfill](http://babeljs.io/docs/usage/polyfill/) e.g.:
|
||||
|
||||
* [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
||||
* [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
|
||||
* [Array.includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)
|
||||
* [...](https://github.com/zloirock/core-js#index)
|
||||
|
||||
The configuration is really simple due to the [plugin system](http://babeljs.io/docs/plugins/). You can choose which ECMAScript version and [stage presets](http://babeljs.io/docs/plugins/#presets) you want to use.
|
||||
|
||||
* for the latest ECMAScript version use [babel-preset-env](https://babeljs.io/docs/plugins/preset-env/)
|
||||
* for version 2015 only use [babel-preset-2015](https://babeljs.io/docs/plugins/preset-es2015/)
|
||||
|
||||
To know what you need you can practice ECMAScript 6 by doing it with [es6katas](http://es6katas.org/) and ask [caniuse](http://caniuse.com/).
|
||||
|
||||
If you are using [TypeScript](/languages-and-frameworks/typescript.html), Babel is not necessary since you already get the new features with TypeScript.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Bower"
|
||||
ring: hold
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
[Bower](https://bower.io/) is a package manager for frontend resources such as JavaScript libraries and CSS frameworks. Compared to [npm](https://www.npmjs.com/), it has a somewhat different approach to loading and resolving the packages, resulting in a smaller and cleaner folder structure.
|
||||
|
||||
In small web projects, this approach is good and sufficient, but larger projects will need more dependencies such as task runners or testing frameworks, which are not available through Bower. As most of the frontend libraries are also available through npm, it's not suprising that we ask ourselves why Bower is still needed.
|
||||
|
||||
At AOE, we decided to use npm as the only package manager to avoid having multiple tools doing similar things. Developers only need to deal with one solution, which makes the project easier to maintain.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Client-side error logging"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
More and more business logic is done client-side with various web and app technologies. How do we know if everything works in production? We can easily track backend exceptions in the server logs, but what about client-side errors in the user's browser or mobile app?
|
||||
|
||||
With client-side error logging, we send errors to a central server to see instantly what is going wrong. With this method errors can be found and resolved quickly before they affect even more users.
|
||||
|
||||
At AOE, we use the Open Source solution [Sentry](https://sentry.io/welcome/).io. It can handle multiple projects and teams and integrates well with other services such as Mattemost/Slack and Issue Tracking Systems.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Consul"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
Consul is a lightweight service to provide a service discovery registry with failure detection (health checks) for circuit breakers. It also provides configuration management with key/value storage.\
|
||||
The typical way to use it is that a consul master cluster takes care of the update and write processes and consul clients run locally on the apps host - data is shared accross the complete Consul cluster. The data can be accessed by using DNS and HTTP APIs.
|
||||
|
||||
At AOE, we use Consul for settings distribution with consul-template as a way to do [Settings Injection](/methods-and-patterns/settings-injection.html) during deployment. Consul is also used as service discovery between apps inside [microservice](/methods-and-patterns/microservices.html) environments.
|
||||
|
||||
With Vault there is another tool that can be used to manage and share secrets.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Container-based builds"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
Running your builds in isolated containers keeps your build servers clean. It allows you to even run them with multiple versions of a framework or programming language. You don't need additional machines like you would for running builds with PHP5 or PHP7 at the same time or running some legacy builds.
|
||||
|
||||
Note that you need to think about some kind of caching mechanism for your depenendies to avoid downloading them in every build, which would cause long build times.
|
||||
|
||||
At AOE, we are currently starting to use this approach for building services and it is especially useful if your build has special dependencies. Also, it's possible to use GitLab as a build tool or use Docker with the new Jenkinspipeline. For caching we are evaluating minio as a cache server. We noticed that our builds run quite rapidly and reliably with that. Also, the complexity of the builds decreased since we don't need any workarounds, which were caused by having everything installed on one build server.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Dagger"
|
||||
ring: adopt
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
[Dagger](https://google.github.io/dagger/) is a fully static, compile-time [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection) framework for both Java and Android. [Dagger](https://google.github.io/dagger/) doesn't use reflections at runtime, it saves resources. For us, it is a perfect match for Android development.
|
||||
|
||||
We at AOE use it as a base framework for every Android project.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: "Datadog"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
After realizing that AWS CloudWatch isn't flexible enough, and running our own metrics aggregation, monitoring and altering isn't something we want to do ourselves, we decided to give Datadog a try. Datadog is very simple to set up and retrieves metrics from the AWS API (and many other integrations) and from an agent running on the EC2 instances. On top of that, it comes with many plugins for services such as Apache, NGINX and ElasticSearch, allowing us to track all important metrics without much effort. Creating dashboards, setting up alarms and integrating into other applications (such as ticket systems) is easy to do and works fine.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "Decoupling Infrastructure via Messaging"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
In [Microservices](/methods-and-patterns/microservices.html) we have already covered the trend that modern architectures are moving away more and more from big monolithic applications to distributed software suites. The result of splitting our software and infrastructure in smaller parts, is the need to communicate with each other. This can be done by direct communication or by message-based asynchronouous communication. While synchronuous communication allows for more plannable "real-time" response times of the overall systems, asynchronouos communication increases the resilience and stability of the system significantly and allows one to use other integration and scaling patterns. However, it often comes with additional complexity.
|
||||
|
||||
Most of the IaaS Cloud providers offer messaging services such as AWS SQS which provide the possibility to decouple our infrastructure via Messaging. Also, we use [RabbitMQ](/tools/rabbitmq.html) as a Messaging and Broker solution within our applications. The decision of using messaging and messaging patterns as an integration strategy can be made as part of [strategic design](/methods-and-patterns/strategic-domain-driven-design.html) considerations.
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: "Devops practices"
|
||||
ring: adopt
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
DevOps is a term that has been around for some years now. We understand DevOps as a philosophy and culture with related practices and tools - all with the aim of bringing (IT) Operations closer to Development.
|
||||
|
||||
Jez Humble described the devops movement like this: "a cross-functional community of practice dedicated to the study of building, evolving and operating rapidly changing, secure, resilient systems at scale".
|
||||
|
||||
With the size of software projects and the effects of agile development, the need to also deliver operation and infrastructure in an agile way increases more and more.
|
||||
|
||||
We have been using the following practices with success:
|
||||
|
||||
**Crossfunctional Teams "you build it, you run it"**
|
||||
|
||||
In the past year, we have moved from a more centralistic or standanlone IT and operations service team to crossfunctional teams with Infrastructure experts working in and with the development team (admins joining the project team).
|
||||
|
||||
And, we changed to crossfunctional teams and a "you build it, you run it" approach for the bigger projects. We have seen that this leads to the following positive effects:
|
||||
* Software application architecture demands a certain infrastructure and the other way around. Having all the know-how in one team leads to more major decisions and implementations. Also, solving of root causes for problems works better.
|
||||
* Rotating operation and incident management inside the whole team brings everyone into closer contact with the day-to-day operation of their software. This results in a shared and improved responsibility and commitment to the complete platform in the team. In addition, this brings developers into contact with the customer - which is an important feedback loop as well.
|
||||
* Increased flexibility in the infrastructure: Implementations and adjustments in the infrastructure are faster and can be done together with the ongoing agile development of the platform.
|
||||
* Developers also explicitly think of operation issues when building the application - since they are responsible for operation. For example, logging concept, monitoring aspects and resilience patterns are now explicitly optimized continuously and improve faster.
|
||||
Important enabler of such an approach is the size and available budget for the project (not every project allows for having a continuous crossfunctional teams that carries out ongoing development and operations). Also, this requires a certain amount of independence for the team.
|
||||
|
||||
As always, we are establishing "community of interests" to improve and promote the knowledge transfer between different teams.
|
||||
|
||||
**Increase of relevant tools**
|
||||
|
||||
Another important aspect and also enabler of DevOps practices is the increase of certain tool and methods - some of them are also represented in the Tech Radar. For example: Puppet Environments; Docker; Cloud Services, Terraform, Consul etc.
|
||||
|
||||
**DevSetup = Prod Setup, [Infrastructure as a Code](methods-and-patterns/infrastructure-as-code.html)**
|
||||
|
||||
Keeping the development infrastructure setup close to production is also a commonly implemented practice and a direct result of the "Infrastructure as Code" method. Handling infrastructure and the required changes and innovations in ways similar to those used for applications is important; you can ready more about this here: Infrastructure as Code
|
||||
|
||||
We encourage all teams to adopt devops practices in the teams and to take care that there is a true collaboration between the different experts in a team and no invisible wall.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Docker"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
Docker is currently the most-used solution for creating and managing container-based infrastructures and deployments.
|
||||
|
||||
Essentially, Docker is a platform to build container images, distribute them and run them as an isolated process (using Linux kernel cgroups, network namespaces and custom mounts).
|
||||
|
||||
In a DevOps environment, this helps a lot as we can run the exact same software and runtime (such as PHP) on both production and locally while developing. This enables us to debug our software much easier.
|
||||
|
||||
Also, Docker allows us to keep our development setup much smaller and faster; instead of VirtualBox setups on a per-project base, we can compose our project development setup out of small containers. A CI environment building the containers allows us to package and test the whole environment instead of different software components on different runtimes in a much more stable way.
|
||||
|
||||
Backed by services such as [Kubernetes](/platforms-and-aoe-services/kubernetes.html), we can deploy Docker containers on a flexible infrastructure and enable our developers to test their software more easily in different environments.
|
||||
|
||||
Here at AOE, we assess Docker in different projects to become more flexible and faster, which increases our focus on development of even better and more stable software.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "Elasticsearch"
|
||||
ring: trial
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
Elasticsearch is a REST-based search and analytics engine based on Lucene. Unlike its competitor Apache Solr, it was developed in the beginning with clustering and scaling in mind. It allows you to create complex queries while still delivering results very fast.
|
||||
|
||||
At AOE, we use Elasticsearch for logging as well as our own search solution [Searchperience®](http://www.searchperience.com/). We recently moved the Searchperience stack from Solr to Elasticsearch and think this was the right decision. Especially in terms of scaling, ease of use and performance, Elasticsearch really shines. Also, the API design took some of the learnings from Apache SOLR into account - for example, the queryDSL is a powerful way of describing different search use cases with highly flexible support of aggregations, etc.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "ELK Stack"
|
||||
ring: adopt
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
The company behind Elasticsearch offers a very nice solution for logging and analysis of distributed data such as logfiles.
|
||||
|
||||
In today's increasingly distributed IT systems, it's very helpful to have a central view of what is going on in your systems - and of course nobody can and wants to look in different logfiles on different servers. A central logging solution provides the option to detect potential relationships between different events more easily. Also, also it can be used to extract useful KPIs or to visualize information on dashboards.
|
||||
|
||||
The abbreviation "[ELK](https://www.elastic.co/products) Stack" stands for the Tools <u>E</u>lasticsearch, <u>L</u>ogstash and <u>K</u>ibana: Together, they provide a solution for collecting data the ability to search, visualize and analyze data in real time.
|
||||
|
||||
Logstash is used to process and forward different data (or logfile) formats. <u>E</u>lasticsearch is used as a search index and together with the Kibana plugin you can configure highly individual dashboards. Recently, there are also the Beats Tools joining this toolstack to ship data to Elasticsearch.
|
||||
|
||||
We have been using the ELK Stack for several years now in several projects and different infrastructure setups - we use it to visualize traffic, certain KPIs or just to analyze and search in application logs. We encourage all teams to use such a solution and take care to write useful logs in your applications.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "Evil User Stories"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
With Evil User Stories, we aim to raise the project teams' (PO, Dev-Team, QA) and clients' awareness for security topics and introduce a security-by-design principle.
|
||||
|
||||
The first step is to identify business use cases of potential vulnerabilities in our software product. The next step is to write an Evil User Story for this use case, from the perspective of an evil persona, e.g. "John Badboy who wants to hack our software". The idea behind this is to take a look at specific parts (business logic) of the software from a perspective that would otherwise not be considered when working on standard user stories.
|
||||
|
||||
So how would this work? To illustrate this, let's consider the following user story: "As Emma Shopping I am be able to pay for a product in my checkout using a credit card". To get that story done, we might have to persist some payment data somewhere. But within the context of an Evil user story we now also need to consider the security for the credit card and payment handling in our application. So, for that reason, we write an Evil User Story, which in this case could, for example, be "As John Badboy, I want to steal payment data" or more specifically "As John Badboy, I want to do to sql inject to get the payment token".
|
||||
|
||||
Before implementation of this particular user story starts, developers should think about how they can secure potentially vulnerable parts of the software to prevent attacks such as sql injections. In this case, one approach should be the use of prepared statements for sql queries. When the development is finished, we should then be able to test the story using an automated testing approach with a penetration testing tool such as [sqlmap](http://sqlmap.org/) to confirm that our database queries are not vulnerable to sql injections.
|
||||
|
||||
Additionally, both solutions should be checked during the development process using code reviews to identify and correct potentially buggy code.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: "Explicit test strategy"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
According to the [ISTQB Glossar](http://glossar.german-testing-board.info/#teststrategie)- a **Test Strategy** is an abstract specification that comprises the designated test levels (unit, integration, system and acceptance tests) and the implementation of each level for a whole organization or for an application. This test strategy can be applicable to one or more projects.
|
||||
|
||||
At AOE, we established an explicit test strategy for many of our projects. The coordination of the test levels improves the effectivity of test runs and helps to avoid testing gaps, double inspection and overhead. Every test level has a different focus. Tests that are executed on one level don't have to be implemented on others.
|
||||
|
||||
These are the test levels that we implement as a standard in the software deployment pipeline of our projects and that handle multiple integrated components and services:
|
||||
|
||||
- **Unit Test:** The unit level tests verify the functionality of a specific section of code, usually at the function level. We use static as well as dynamic test methods such as code reviews, style or complexity checks and white-box testing.
|
||||
- **Module Tests:** Module Tests focus on testing the functionality that a service or component provides in isolation to other components or services that this service depends on. This test stage finds errors in a component. It should never fail due to a consumed service that is not reachable or has been altered. Therefore, all dependencies of these components are mocked or stubbed on some level. Tests are most commonly conducted through interfaces using black-box testing.
|
||||
- **Integration Tests:** On the integration level, individual software modules are combined and tested as a group. The integration testing verifies functional, performance and reliability requirements. These tests are also most commonly conducted through interfaces using black-box testing. In case there is a great number of (external) subsystems, we mock these systems outside of the defined context and use contract-based testing to verify the interfaces. All contract-based tests that focus on testing the interface contracts between services are also executed on this test level.
|
||||
- **System Level Tests:** On the system level, tests are performed on a complete, integrated system, where they evaluate the system's compliance with its specified requirements. System tests not only verify the design, but they also check the system's behavior in general and even the assumed expectations of the customer. They are intended to test up to and beyond the bounds defined by the explicit system requirements.
|
||||
- **Client Acceptance Tests:** The client acceptance level includes all testing done by the customer and is the last one in the succession of the five test levels. The objective is to evaluate the system's compliance with the business requirements and to assess whether it is acceptable for delivery.
|
||||
|
||||
As a rule, we automate the execution of tests where it is feasible and sensible. Related to the test strategy are the test concept, test data management and the usage of a test case management tool that allows one to assess and categorize functional test cases.
|
||||
|
||||
Due to the practical usefulness of having a sound test strategy for a project, we classify the explicit test strategy for projects with assess.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "Flow"
|
||||
ring: hold
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
[Flow](https://flow.neos.io/) is a PHP web application framework developed for the [Neos](https://www.neos.io/) project.
|
||||
|
||||
We used Flow in a couple of projects and decided to put it on hold for the following reasons:
|
||||
|
||||
* slow during development and hard to debug because of its need to "compile" the application to integrate e.g. AOP
|
||||
* limited documentation and community
|
||||
* development of the framework is closely coupled to the progress of the Neos project
|
||||
|
||||
Although it could be that some of the above-mentioned aspects have improved in the past, we decided to use other PHP frameworks such as [Symfony](http://symfony.com/) or other Languages (See [Go](/languages-and-frameworks/go-lang.html); [Play Framework](/languages-and-frameworks/play-framework.html); [Spring Boot](/languages-and-frameworks/spring-boot.html))
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Galen"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
With [Galen Framework](http://galenframework.com/), layout testing can be automated to save you a lot of manual work. With its own specification language (Galen Spec), you can write tests to verify the correct look of the web page as well as the location and alignment of specific elements on a page.
|
||||
|
||||
So, you can write simple tests such as "The button should be green" as well as more complex behavior specifications such as "On mobile devices the button should be inside the viewport". Especially when testing a responsive website on multiple devices, browsers and resolutions, the manual testing effort gets expensive. To help with that, Galen runs its specifications fully automated with Selenium against the required browsers and devices.
|
||||
|
||||
Whenever a test fails Galen writes a test report with screenshots to show the mismatching areas on the page to help testers and developers become aware of the problem.
|
||||
|
||||
At AOE, the Galen Framework helps us to continuously test the UI for potential regression bugs introduced by new features.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Gatling"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[Gatling](http://gatling.io/) is a highly capable load testing tool. It is designed for ease of use, maintainability and high performance.
|
||||
|
||||
Out of the box, Gatling comes with excellent support of the HTTP protocol that makes it a tool of choice for load testing any HTTP server. As the core engine is actually protocol agnostic, it is perfectly possible to implement support for other protocols. For example, Gatling currently also ships [JMS support](http://gatling.io/docs/current/).
|
||||
|
||||
Gatling is built with [Scala Lang](https://extranet.aoe.com/confluence/display/knowledge/Scala+Lang) and [Akka](https://extranet.aoe.com/confluence/display/knowledge/Akka). By making good use of Scala's native language features (such as as the extensive type system), it makes writing tests feel natural and expressive, instead of writing load tests based on a DSL encoded in some special syntax.
|
||||
|
||||
This allows us to use all native Scala features to work with, with the focus on the ability to structure your tests as pure code, and actually unit test your load tests.
|
||||
|
||||
Besides the very good performance, we definitely like the pure code-based approach. Gatling creates HTML-based reports with nice graphs and metrics about how and what was tested.
|
||||
|
||||
We use Gatling as an alternative to Jmeter with success in some of our projects. We encourage teams to try Gatling for future load testing. There is an integrated test recorder similiar to what other test frameworks have to get you started with a basic test case.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Go / Golang"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
2016 was the year of Go, with a lot of Open Source projects gaining a lot of attention and many companies started to use it.
|
||||
|
||||
Go went from #54 to #13 on the [TIOBE index](http://www.tiobe.com/tiobe-index/) in January 2017, and it became the TIOBE programming language of the year 2016.
|
||||
|
||||
Here at AOE, we use several services written in Go on a daily basis, such as Mattermost, Docker, Consul and Kubernetes. Also, more and more applications, such as Gitlab, incorporate Go-based services to "off load" heavy work.
|
||||
|
||||
Go, as a programming language, has some very interesting features such as native support for concurrency (go routines), static compiled binaries with a very small memory footprint, cross compiling and much more. A big advantage of Go is the very flat learning curve, which allows developers from more dynamic languages such as PHP to be proficient in a very short time.
|
||||
|
||||
If you want to get a feeling for Go, you should start with the [online tour](https://tour.golang.org/welcome/1), within a day you'll have a good understanding of the core concepts, syntax, etc. - that is also because the language often tries to provide only one simple way of doing things; an example for this is that code formatting and styling is defined (yet not enforced as in Python). Part of this is also that Go itself is very opinionated: So, for example, for object oriented programming in Go, composition is the prefered way of defining data structures, and some might miss advanced concepts such as inheritance.
|
||||
|
||||
We currently use Go for projects and microservices where we need flexibility and performance.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "Gradle"
|
||||
ring: adopt
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
Gradle is a build automation tool originating in the Java space, providing declarative dependency management (like Maven) and support for custom functionality (like Ant). It has superb multi-project support and is extremely extensible via third-party plugins and also via self-written extensions and plugins that make it outstanding in its area.
|
||||
|
||||
It uses a Groovy-based DSL to declaratively model your problem domain (Build automation) and provides a rich object model with extension points to customize the build logic. Because it is extremely easy to extend this DSL, you can easily provide a declarative interface to your customizations and add-ons.
|
||||
|
||||
While providing plugins for building libs, apps and webapps in Java, Groovy and Scala out of the box it is not tied to the JVM as target platform, which is impressively shown by the native build support for C / C++.
|
||||
|
||||
At AOE, it is used in various places already: to build [Anypoint](/tools/anypoint-platform.html)- and [Spring Boot-](/languages-and-frameworks/spring-boot.html) based applications; to build Android Apps; to automate the creation of Jenkins Jobs; to create Docker images and Debian packages and also do some deployment scripting with it.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Groovy"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
Groovy is a dynamically typed compiled language running on the JVM. It is easy to learn as it provides a familiar syntax for Java programmers, but also offers advanced features such as closures and makes some mandatory Java syntax requirements optional to enhance the conciseness of the code. These features make Groovy especially well-suited for scripting and domain-specific languages. This is used by popular tools such as Gradle or Spock.
|
||||
|
||||
At AOE, Groovy is used in many projects and areas. We use Gradle as a build system, we carry out unit and integration testing with Spock and Geb, we generate Jenkins jobs with JobDSL and we implement complete services with Groovy and [Spring Boot](/languages-and-frameworks/spring-boot.html).
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
title: "Grunt"
|
||||
ring: hold
|
||||
quadrant: tools
|
||||
---
|
||||
|
||||
|
||||
Grunt is a JavaScript task runner that automates repetitive tasks. While Grunt served us well for a good amount of projects,
|
||||
other alternatives such as [Gulp](http://gulpjs.com/) emerged in the meantime and proved to be a better pick for the
|
||||
majority of our teams.
|
||||
|
||||
We have two main reasons for discarding Grunt in favor of other tools:
|
||||
|
||||
### Speed
|
||||
If a decent amount of tasks is reached, Grunt is known to run slower than other tools, because it heavily relies on I/O operations and
|
||||
always stores the result of one task as files on the disk.
|
||||
|
||||
### Configuration
|
||||
On large projects where a lot of automation is required, it can get very tedious to maintain complex and parallel running tasks.
|
||||
The grunt configuration files sometimes simply don´t gave us the flexibility that we needed.
|
||||
|
||||
Currently our preferred way to go is either simply use [NPM scripts](https://docs.npmjs.com/misc/scripts) or rely on [Webpack loaders](https://webpack.js.org/concepts/loaders/) for file preprocessing. For non-webpack projects we also utilize Gulp.
|
||||
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
title: "Gulp"
|
||||
ring: adopt
|
||||
quadrant: tools
|
||||
---
|
||||
|
||||
Gulp is a javascript task runner much like Grunt. The tasks are written in javascript code.
|
||||
|
||||
It is a tool that helps you automate numerous tasks surrounding web development. A typical use is to configure preprocessors for Sass, to compile CSS or to optimize CSS, Javascript and Images.
|
||||
|
||||
With Gulp and its many plugins you can also do stuff such as start a web server and reload the browser if changes happen.
|
||||
|
||||
To get started you need to install Gulp on your machine via npm.
|
||||
|
||||
```javascript
|
||||
npm install gulp -g
|
||||
|
||||
```
|
||||
|
||||
|
||||
You also need it locally in your project, so you have to install it as a dependency in your project .
|
||||
|
||||
```javascript
|
||||
npm install gulp --save-dev
|
||||
|
||||
```
|
||||
|
||||
You can split your tasks into various smaller sub-tasks and even split it up into smaller files.
|
||||
|
||||
A basic Gulp task can look like this:
|
||||
|
||||
```javascript
|
||||
const gulp = require('gulp');
|
||||
// Requires the gulp-sass plugin
|
||||
const sass = require('gulp-sass');
|
||||
const autoprefixer = require('gulp-autoprefixer');
|
||||
const cssnano = require('gulp-cssnano');
|
||||
|
||||
gulp.task('sass', function(){
|
||||
return gulp.src('app/scss/**/*.scss') // tell gulp where your source files are
|
||||
.pipe(sass()) // Converts sass into css with the help of a gulp plugin called gulp-sass
|
||||
.pipe(autoprefixer({browsers: ['last 2 versions']})) // auto prefixes the css for the last 2 versions of browser, like ie9 specific css
|
||||
.pipe(cssnano()) // minify the css
|
||||
.pipe(gulp.dest('app/css')) // tell gulp where to put the converted file. this is the first time where a file is written
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
you can now run this task simply by executing the following command in your terminal:
|
||||
|
||||
```javascript
|
||||
gulp sass
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "HAL / HATEOAS"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
Hypermedia As The Engine Of Application State or in short HATEOAS is a pattern that helps to organize dependencies and resources in a RESTful API. The basic idea of HATEOAS is that an API consumer do not have to know how dependencies of resources are connected and how to get them. A consumer must only be familiar with the basics of hypermedia.
|
||||
|
||||
Let's assume we have a bank account and an action to deposit money on that account. Everything you need to know is that the account resource has an action for a deposit. The URL of that action can then fetched from the link attribute with the corresponding relation.
|
||||
|
||||
```
|
||||
<account>
|
||||
<account_number>12345</account_number>
|
||||
<balance currency="usd">-25.00</balance>
|
||||
<link rel="deposit" href="https://bank.example.com/account/12345/deposit" />
|
||||
</account>
|
||||
```
|
||||
|
||||
Besides from HATEOAS there is an alternative implementation called Hypertext Application Language, in short HAL, which has much more features than the basic HATEOAS.
|
||||
|
||||
With HAL you are allowed to also define parametrized links, embedded resources and documentation relations (which are called curies). You can find the specification here.
|
||||
[http://stateless.co/hal_specification.html](http://stateless.co/hal_specification.html)
|
||||
|
||||
If you want to link different api endpoints or ressource locations in your API responses you should use this standard.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Hystrix "
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
Hystrix is a very powerful library for handling failures, fallbacks and latency management within complex distributed environments. Netflix developed it and after years of experience, they are using it in almost each of their microservices. It evolved to a great library for handling resilience in complex architectures and covers solutions for the most common resilience patterns like:
|
||||
|
||||
- Fail fasts
|
||||
- Fail silent
|
||||
- Circuit Breaker
|
||||
- Fallbacks (Static, Stubbed)
|
||||
|
||||
Beside from that purposes Hystrix also offers some helpful features like parallel and asynchronous execution, In-Request-Caching and other useful features for working with distributed systems.
|
||||
|
||||
Another useful component that you are able to use with Hystrix is his dashboard that give you the ability of real time monitoring of external dependencies and how they behave. Alerting is also able via the dashboard.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "imgix"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
[Imgix](https://www.imgix.com/) is an SaaS solution for delivering and processing images. When developing responsive websites, you will quickly reach the point where you need various versions of your images to achieve a good responsive user interface. You want high quality versions for retina displays but small versions for mobile devices with a slow Internet connection.
|
||||
|
||||
Especially when dealing with user-generated uploads, it is getting hard to create different versions for any supported device and breakpoint of your web page. Doing this manually is hardly an option.
|
||||
|
||||
At AOE, we decided to use imgix as an image processing service for some projects to solve this problem. The benefits of imgix are the simple API to create responsive images in real-time as well as the fast delivery over their CDN.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Infrastructure as Code"
|
||||
ring: adopt
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
Infrastructure as Code (IaC) describes the process of managing all infrastructure resources via code. Treating infrastructure code the same way we treat application code, we can benefit from the same advantages of having a history in our version control system, doing code reviews and rolling out updates via a Continuous Delivery pipeline in a way that closely approaches how we handle application deployments.
|
||||
|
||||
Infrastructure code is often described in a declarative language und the target platforms figure out what to create, update or delete in order to get to the desired state, while doing this in a safe and efficient way. We've worked with [AWS CloudFormation](https://aws.amazon.com/de/cloudformation/) in the past, and while this is a great tool, you can only manage AWS resources with it and you need some more tooling around it in order to automate things nicely and embed it into other processes such as Jenkins Jobs. That's what we created [StackFormation](https://github.com/AOEpeople/StackFormation) for. Another tool that is actively developed is [Terraform](https://www.terraform.io/). Terraform comes with a lot of concepts that make managing environments easier out of the box and nicely embeds into other related tools. Also, Terraform allows you to manage a variety of different infrastructure providers.
|
||||
|
||||
Infrastructure as code should cover everything from orchestration of your infrastructure resources, networking and provisioning as well as monitoring setup. The orchestration tools mentioned above are supplemented by other tools such as Puppet, Chef or simple Bash scripts that take over provisioning the instances after they are booted.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Jest "
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[Jest](https://facebook.github.io/jest/) is a javascript testing framework by facebook to test javascript code **and** react applications / components.
|
||||
|
||||
We started using Jest (and [watchmen](https://github.com/facebook/watchman)) instead of Karma because it:
|
||||
|
||||
- gives us integrated mocking library
|
||||
- gives us integrated support for testing "promises"
|
||||
- gives us integrated code coverage report
|
||||
- automatically runs tests related to changed files (instead of all tests)
|
||||
- gives us parallel test execution
|
||||
- gives us snapshot testing for react components
|
||||
|
||||
It is easy to set up. And even if you have a running setup with karma/chai you can easily replace karma with jest. With a small [workaround](https://medium.com/@RubenOostinga/combining-chai-and-jest-matchers-d12d1ffd0303#.3callo273), chai and jest test matchers work fine together.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "Job DSL (Jenkins)"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
The [Job DSL ](https://wiki.jenkins-ci.org/display/JENKINS/Job+DSL+Plugin)is a plugin for the Jenkins automation server. Jenkins jobs that automate parts of a software project are usually configured using the web interface of Jenkins. If Jenkins is the choice for your project and the number of build jobs tend to grow, the Job DSL plugin is your friend.
|
||||
|
||||
The plugin allows Jenkins jobs to be described by code (Groovy DSL). This code is then used for generating Jenkins jobs. As a consequence, job configuration can be part of the project's source code. During the generation step, existing jobs are synchronized, overwritten or left alone, depending on the configuration. The same configuration manages deleting or ignoring jobs that are not described in code anymore. Jobs can easily be restored in case of data loss and changed without clicking buttons for hours. The automation also makes it easy to seed large numbers of homogeneous components and builds on different branches.
|
||||
|
||||
The ability to treat Jenkins jobs as code is a big advantage. We highly suggest that every team automate the setup of their jobs and their pipelines. Another way of expressing build pipelines as code is the new [Jenkins Pipeline](https://jenkins.io/doc/book/pipeline/) feature - but still we see the need of Job DSL seeder jobs to seed the Jenkins pipeline jobs themselves and any additional jobs.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Keycloak"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
User management, authentication, authorization and Single Sign-On are part of most distributed systems nowadays. Building these sensitive and serious parts on your own might be a problem due to knowledge- and budget restrictions. Because of growing requirements in that field (social logins, single sign-on, federation, two-factor authentication, etc.), as well as growing security concerns, building these things on your own has become more challenging during the past decade.
|
||||
|
||||
As a consequence, the recommendation is: use an existing solution and connect it with your project's codebase using provided standards. Our recommended solution is the Open Source project JBoss Keycloak. We use Keycloak in our OM3 suite for several authentication-related use cases - such as user management for system users and single sign-on for customers. The OAuth access tokens can be used to secure APIs that access sensitive information.
|
||||
|
||||
Keyloak is based on standards such as OAuth2, OIDC and SAML2. Securing a distributed system is supported by adapters, which are provided by the Keycloak developers for different technology stacks. If there is no adapter for your technology stack, an integration on protocol level with a library is simple. A lot of configurable features require no coding in the integrated projects.
|
||||
|
||||
By design, the Keycloak project offers customizability and extensibility via so-called SPIs, e.g. a custom authenticator can be implemented to address project specific problems.
|
||||
|
||||
Keycloak normally runs standalone and can use various database products. A docker image is available to start in a containerized environment.
|
||||
|
||||
Keycloak might be overkill, depending on your project needs. For a simple integration with, for instance, a social login provider (Facebock, Twitter, etc.) Keycloak might be too much. For a JVM project, the pac4j library might be an alternative. If a cloud-based solution is preferred and data privacy concerns are not an issue, Auth0 might be the choice.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Kubernetes"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
Kubernetes is a container orchestration platform, which supports many different infrastructure providers. It allows you to deploy containers and takes care of running, scaling or self-healing your applications based on configurations you provide. It's based on years of knowledge and experience Google gained by using containers.
|
||||
|
||||
At AOE, we started Kubernetes in a test environment on bare metal to experiment with it. It's currently used for running AOE internal apps such as dashboards as well as running builds in containers. We also started to use it for upcoming projects to run and manage several services. There are Tools to automate the setup of kubernetes in AWS like [Cops](https://kubernetes.io/docs/getting-started-guides/kops/). Another helpful tool is [Minikube](https://github.com/kubernetes/minikube), which allows to test and run kubernetes locally.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Maintain third party packages"
|
||||
ring: hold
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
Rebuilding and packaging software from "third parties" (e.g. PHP, MySQL, Redis, Nginx, Java,...) implies starting to maintain the packaging for the desired distribution.
|
||||
|
||||
Even with tool support and targeted for automation, we found that building those packages is very often unstable. The effort to keep up with the upstream changes (security changes, fixes, etc...) exceeds the benefit in most cases. We prefer to not create our own packages and rather use what's available in the distribution repository.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Microservices"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
|
||||
Microservices as an architecture style is getting very popular recently. At AOE, more and more teams are adding microservices to their existing application architecture or designing applications with microservices.
|
||||
|
||||
We also like the term "self-contained systems" instead of microservices.
|
||||
|
||||
The benefits we see are:
|
||||
|
||||
* better handling of complexity compared to adding features in a monolithic approach
|
||||
* beeing able to use the languages and framework that best fit the purpose of the service
|
||||
* enabling better parallel work in big teams or multi-team projects
|
||||
* flexibility in deploying changes to production - by just deploying the changed service
|
||||
|
||||
Related patterns are [Strategic Domain Driven Design](/methods-and-patterns/strategic-domain-driven-design.html) as an approach to wisely cut your architecture according to useful bounded contexts and decide on the relevant communication and "translation" between the services.
|
||||
In case you are looking for a small visualisation tool for your microservice architecture you might find [vistecture](https://github.com/AOEpeople/vistecture/) useful.
|
||||
|
||||
Also [Resilience thinking](/methods-and-patterns/resilience-thinking.html) is especially important when designing an application as a suite of microservices.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Neo4j"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
Neo4j is one of the oldest Open Source Graph Databases. It's one of the rare NoSQL databases that is fully ACID-compliant. We see two main advantages of graph databases:
|
||||
|
||||
* for a lot of domains there is a natural way of modeling this in a graph (the Neo4j website says "everything is a graph"),
|
||||
* and querying relations between nodes is very efficient in a graph database.
|
||||
|
||||
Neo4j database is implemented in Java and can therefore be embedded in your application if you live on the JVM.
|
||||
|
||||
You can also choose to run it in a classic server mode, which then provides you with the possibility to either use its REST API or connect to it via the BOLT Driver, which has native bindings for the most popular languages.
|
||||
|
||||
The cypher query language which comes with Neo4j is a declarative graph query language that allows for expressive and efficient querying and updating of the graph.
|
||||
|
||||
At AOE, we use Neo4j mostly for explorative, interactive work with weakly structured or highly connected data, also we are evaluating this for knowledge-based recommendations in our [Searchperience](http://www.searchperience.de/home.html) product.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "node.js"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
Node.js is a no- browser JavaScript execution runtime. Its basis is Google's V8 engine. [Node](https://nodejs.org/en/) is event-driven and follows a non-blocking I/O model.
|
||||
|
||||
It’s a good choice for restful APIs, realtime purposes or situations where many concurrent connections are expected, where each connection has a lightweight memory footprint.
|
||||
|
||||
Node allows separation of concerns by using its package manager [npm](https://www.npmjs.com/), which is also the largest ecosystem of Open Source libraries (modules).
|
||||
|
||||
Modules are added as dependencies and offer a wide range of functionalities in a range from simple helper functions to mature web frameworks such as [express.js](http://expressjs.com/de/).
|
||||
|
||||
Many PaaS providers (AWS, Google Cloud Platform, Azure) support node, including deployment and monitoring services out of the box for scalable stateless applications.
|
||||
|
||||
At AOE, we successfully use node.js-based applications for smaller services or internal tools such dashboards.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: "NPM"
|
||||
ring: adopt
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[NPM](https://www.npmjs.com/) is one of, if not the most, popular package manager for JavaScript. Because of the big community, you can find nearly every dependency in npm.
|
||||
|
||||
Instead of other package managers such as [bower](/tools/bower.html), you have to write your packages as [modules](https://en.wikipedia.org/wiki/CommonJS). This unifies the way you have to use, test and, of course, understand dependencies.
|
||||
|
||||
NPM creates a tree for your dependencies and their nesting dependencies. Because of this, you don't need to handle version conflicts, since every dependency uses there own version of e.g. [webpack](/tools/webpack.html).
|
||||
|
||||
With [shrinkwrap](https://docs.npmjs.com/cli/shrinkwrap) you have a robust tool to lock down and manage the versions of your dependencies - following the [Pin (external) dependencies](/methods-and-patterns/pin-external-dependencies.html) approach.
|
||||
|
||||
For each package you have to classify your dependencies:
|
||||
|
||||
- dependencies are needed for use without the need of pre compiling, e.g. [lodash](https://lodash.com/)
|
||||
- devDependencies are needed for development only, e.g. testing frameworks or pre compiler e.g. [babel](/languages-and-frameworks/babel.html)
|
||||
- peerDependencies you have to provide for using the package
|
||||
|
||||
With [scripts](https://docs.npmjs.com/misc/scripts) you get support for the most common build lifecycle steps, e.g. build, start, test ...
|
||||
|
||||
Other useful features:
|
||||
|
||||
- mirror support for your own repository (e.g. [artifactory](/platforms-and-aoe-services/artifactory.html))
|
||||
- can be used for server and client JavaScript development (see [node.js](/languages-and-frameworks/node-js.html) )
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "Oro Platform"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
OroPlatform is a framework built on Symfony 2 with the purpose of providing the features you need in every business application that is not your core business logic. Hence, it serves you with a basic application, providing login and complex security, menus and menu management, history, audit trails, settings management, etc. It comes complete with a design and many widgets to be utilized in own entities. Other Features of OroPlatform are, for example, a WebSocket server-driven user interface, queue-based task runners, REST Interface, as well as messaging- and workflow systems.
|
||||
|
||||
One of the central features is that entities, which are to be managed within the system, can be set up completely by configuring them using the UI. This in itself implies that it puts another abstraction layer upon doctrine and symfony defaults.
|
||||
|
||||
As with every framework or application, the general-purpose goals and abstraction comes with drawbacks: In fact, OroPlatform modifies and extends the common way of doing things in Symfony in several places, which makes the developer's life hard at times. Also, the UI and package managing are set in such a way that they are hard to extend or replace. The many additional abstraction layers can result in decreased performance.
|
||||
|
||||
On the other hand, OroPlatform gives you a good headstart for prototyping and frees you from rebuilding common requirements - which makes it a relevant choice for business applications with the need to manage several entities in a backend. Also, projects such [Akeneo](/tools/akeneo.html) or OroCRM use OroPlatform with success.
|
||||
|
||||
Since the project is still young, the future development and improvements need to be watched. We classified the Framework as ***Assess***.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "Pair working"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
We summarized the practices of pair programming and administrating as pair working.
|
||||
|
||||
Derived as a practice from eXtreme Programming (XP), pair programming is a method/pattern that aims for fine-scaled feedback within a team.
|
||||
|
||||
At AOE, some developers and operators work in pairs, not constantly, but from time to time. Most teams have positive experiences using this method, but not all teams tried the by-the-book-approach (driver and navigator principle). Especially for non-trival tasks, pair working results in rapid knowlegde exchange and better results with less bugs. We encourage the teams to try this approach more often.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "phan"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
Phan is a static code analyzer for PHP7, which is very fast, since it uses the PHP 7 AST (abstract syntax tree). Phan basically offers some of the safety that otherwise only compiled type-safe languages have - such as checking function references and return types.
|
||||
|
||||
We expect at least the following benefits:
|
||||
|
||||
- Decreased bug density; possible bugs and issues are found early
|
||||
- Safer code and higher code quality
|
||||
|
||||
We think Phan can be used in the deployment pipeline or as commit hooks for PHP 7-based applications. For a full Feature list check [here](https://github.com/etsy/phan#features).
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "PHP7 over PHP5"
|
||||
ring: adopt
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
PHP 5 has been around for a very long time, and can be considered as the PHP version that defined where PHP wants to go in the future.
|
||||
With proper OOP, support for clojures and a steadily improving type system, it has become a very mature language.
|
||||
However, in the past 3 years, Facebook introduced HHVM, which became a major influence on PHP 7 and eventually brought a lot of improvements not only for the execution speed, but also with proper type hints and other features.
|
||||
|
||||
Here at AOE, we have numerous PHP projects, and we often kept it backwards-compatible to make sure that it will run on older systems. This is comparable to the procedure most frameworks (Magento, OroPlatform and derived projects) use.
|
||||
|
||||
Now, PHP 5 has reached its end--of-life, and it is time to discontinue the backqards-compatibility in favor of better and more stable applications.
|
||||
Even though we can use the PHP 7 runtime while being PHP 5-compatible, it is not considered good practice anymore, as we can now rely on the PHP 7 features and use all of its advantages.
|
||||
|
||||
One of the major points PHP 7 supports is proper typehinting and return types (apart from PhpDocs), which makes [static analysis](/tools/phan.html) much easier and can improve the overall code quality significantly.
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
title: "Pin external dependencies"
|
||||
ring: adopt
|
||||
quadrant: methods-and-patterns
|
||||
---
|
||||
|
||||
A lot of applications have dependencies on other modules or components. We have
|
||||
used different approaches regarding how and when these dependencies are resolved
|
||||
and have agreed on using a method we call "Pin (External) dependencies".
|
||||
|
||||
This is especially relevant for script languages, where the dependency
|
||||
management references the code and not immutable prebuild binaries - and
|
||||
therefore resolves the complete transient dependencies on the fly.
|
||||
|
||||
Most of these package- or dependency management solutions support two artefacts:
|
||||
|
||||
* a semantic dependency definition. This defines the compatible versions of the
|
||||
required dependencies. (Composer: composer.json / NPM: package.json)
|
||||
* a lock file defining the exact revisions of the dependencies and the transient
|
||||
dependencies (dependencies of dependencies). This is created after running the
|
||||
tool. (Composer: composer.lock / NPM: npm-shrinkwrap.json / yarn: yarn.lock).
|
||||
|
||||
We suggest the following:
|
||||
|
||||
* Keep the dependency definition AND the lock file in version control. This
|
||||
ensures that chained dependencies are also locked and you have changes of that
|
||||
file visible in your version control commit history. This helps finding issues
|
||||
or bugs that might relate to unintended updates in external modules or
|
||||
transient dependencies.
|
||||
* Build Step: The application build step should use the the pinned versions
|
||||
(with the help of the lock file) to ensure that the same revisions of the
|
||||
dependent packages are used.
|
||||
* It's also suggested to use local or central caches for the retrieval of
|
||||
packages. (E.g.
|
||||
[artifactory as composer and npm cache](/platforms-and-aoe-services/artifactory.html))
|
||||
|
||||
For updating of dependencies define a process in the team. This can either be
|
||||
done on the dev-system or in a seperate automated CI job - both resulting in
|
||||
updated dependency definitions in the applications VCS.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Pipeline as Code"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
Continuous Integration and Delivery is a critical part of our development and deployment process at AOE. Using Jenkins for many years the "instructions" how to build, test and deploy applications were scattered between many custom scripts and the pipeline was often maintained by manual maintenance of Jenkins jobs. Soon, we realized that we need a more native way to express the full CI/CD pipeline process in code and manage it in version control.
|
||||
|
||||
Being an important part of each project, the pipeline configuration should be managed as code and rolled out automatically - this also allows us to manage the pipeline itself applying the same standards that apply to application code.
|
||||
|
||||
While some teams started using Jenkins' [JobDSL plugin,](https://wiki.jenkins-ci.org/display/JENKINS/Job+DSL+Plugin) others explored the new [Jenkins Pipeline](https://jenkins.io/doc/book/pipeline/) - in both ways, the build artifacts should be published to an artifact repository such as [Artifactory.](/platforms-and-aoe-services/artifactory.html)
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "Play Framework"
|
||||
ring: adopt
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
The Play Framework is a lightweight (web)application framework for Java and [Scala](/languages-and-frameworks/scala-lang.html) programmers.
|
||||
|
||||
A developer can choose from different modules to include necessary functionality such s accessing http resources, databases, and so on. As a consequence, the developer can choose, and is not distracted by or clobbered with irrelevant things. This approach is considered as minimalistic, but it is easy to include necessary functionality.
|
||||
|
||||
Regarding the architecture, Play is stateless and built on Akka. As a consequence, Play applications have much lower resource consumption regarding CPU und memory and can scale easily. Play manages concurrency without binding a request to a thread until the response is ready.
|
||||
|
||||
With the use of "[Futures](http://docs.scala-lang.org/overviews/core/futures.html)" in your code you can turn synchronous tasks (such as IO or API call to another service) into asynchronous and you can build non-blocking applications. It is recommended to understand the principles Play uses to achieve performance and scalability.
|
||||
|
||||
Play can act as backend service delivering JSON, for esample. For building web applications. the [Twirl](https://www.playframework.com/documentation/2.5.x/ScalaTemplates) template engine enables server-side rendering of html pages. These html pages can include css and java script parts of your own choice.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "PostCSS"
|
||||
ring: adopt
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
PostCSS is a tool for transforming stylesheets with JavaScript plugins. It comes with a parser that reads your CSS file into an AST, pipes it through the loaded plugins and finally
|
||||
stringifies it back into a (transformed) CSS output file.
|
||||
|
||||
We at AOE love PostCSS because it gives us the power to use [CSS Modules](https://github.com/css-modules/css-modules), which finally ends the curse of global CSS.
|
||||
|
||||
It also has a huge list of more than 350 other [available plugins](http://postcss.parts/).
|
||||
Sure, not all of them are useful, but the sheer number of plugins shows how easy it is to write your own plugin for it.
|
||||
In fact, it´s just a matter of writing a single JS function.
|
||||
|
||||
Finally, PostCSS is very fast and easy to setup because it runs 100% in JavaScript.
|
||||
Compared to [SASS](/languages-and-frameworks/sass.html) as a preprocessor, it feels much more powerful but at the same time less bloated with superfluous functionality because everything comes in its own little plugin
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Protobuf"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
In an increasingly microservice-oriented environment, it is crucial that all parties agree on a common language and wire format for data exchange.
|
||||
|
||||
JSON and XML are two well-known formats for serialization of data; however, they come with a few drawbacks. JSON is completely dynamic without any validation (though there is json-schema) and XML uses an extremely heavyweight syntax, which carries a huge overhead, so parsing and transport becomes quite slow.
|
||||
|
||||
Protobuf, amongst others, is an approach to solving this problem by using well-defined schemas to create language-specific code, which serializes/marshals and deserializes/unmarshals data. One of the key features is the built-in support for evolving schemas; it is easily possible to incrementally extend the definition while staying backwards-compatible and compose messages consisting of several sub-messages.
|
||||
|
||||
If you are looking for a way to have different systems agree on a common protocol on top of a transport layer (such as AMQP or HTTP), Protobuf is definitely worth examining more closely and should be assessed.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: "Puppet Environments"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
Puppet
|
||||
------
|
||||
|
||||
Puppet is an Open Source configuration management tool. It is used by a wide range of different companies world-wide, e.g. the Wikimedia Foundation, Mozilla, Reddit, CERN, Dell, Rackspace, Twitter, the New York Stock Exchange, PayPal, Disney, Citrix Systems, Spotify, Oracle, the University of California Los Angeles, the University of North Texas, QVC, Intel, Google and others.
|
||||
|
||||
Puppet has been the basic tool to address Continuous Configuration Automation (CCA) in AOE's [Infrastructure as Code](/methods-and-patterns/infrastructure-as-code.html) strategy (IaC) for more than 4 years.
|
||||
|
||||
Puppet Environments
|
||||
-------------------
|
||||
|
||||
Intended to give projects the means to develop and maintain their own infrastructure, separated and not influenced by other projects, Puppet environments, together with Puppet module versioning and ENC, have been introduced.\
|
||||
Puppet Environments are rated "Trial". It supports our strategy of Infrastructure as Code (IaC) and links it to our DevOps approach, enabling project teams to set up and customize their own infrastructure.
|
||||
|
||||
Teams that want to use the Puppet Environments service from the AOE IT Team will find detailed information about the implemented CI/CD process for this.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "RabbitMQ"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
RabbitMQ is an Open Source message broker - implementing the Advanced Message Queuing Protocol (AMQP) protocol. It provides a reliable and scalable way to transport data between loosely coupled applications, using different EAI patterns such as the Publish & Subscriber pattern. AMQP supports direct and fan-out exchanges (broadcasts) as well as topics. Queuing mechanisms allow for robust architectures, mitigating the risks of application downtimes. Typically, a RabbitMQ server can easily buffer millions of messages. RabbitMQ supports JMS in addition to AMQP. It is not intended to use JMS for new systems, but it makes RabbitMQ useful for integrating legacy systems.
|
||||
|
||||
There are several alternative solutions to RabbitMQ, e. g. the free Apache ActiveMQ, which is integrated in [Anypoint platform](/tools/anypoint-platform.html). ActiveMQ implements a somewhat simpler routing concept than RabbitMQ, but offers more protocols. Commercial products in this area are offered by IBM (Websphere MQ), Fiorano and almost every vendor of ESB products.
|
||||
|
||||
We use RabbitMQ internally for transferring messages safely in our logging ecosystem between [Logstash](/platforms-and-aoe-services/elk-stack.html) proxies and servers using direct and fan-out exchanges for delivering messages to appropriate destinations. RabbitMQ is also used to asynchronously trigger Jenkins jobs from our SCMs to mitigate heavy load on the SCMs, usually caused by Jenkins polls for SCM changes. Additionally, some critical events for monitoring are using RabbitMQ for guaranteed notification.
|
||||
|
||||
RabbitMQ is rated "Trial". It fits into our approach to build robust, [resilient systems](/methods-and-patterns/resilience-thinking.html) and use [asyncronous messages](/methods-and-patterns/decoupling-infrastructure-via-messaging.html) for loosely coupled communications between components. In practice, RabbitMQ proved to be stable and dealt well with service interruptions from failures and maintenance slots. A common pain point is RabbitMQ as a single point of failure disrupting the data flow in a system. This issue is currently approached by setting up a HA cluster for RabbitMQ. The outcome of this approach will clarify the extent of future usage of RabbitMQ in our systems.
|
||||
|
||||

|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "RAML"
|
||||
ring: adopt
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
[RAML](http://raml.org/) (the RESTful API Modelling Language) is a YAML-based API specification language. It's now available in [version 1.0](https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md#defining-types). The philosophy behind it is to [specify the API before implementation](/methods-and-patterns/api-first-design-approach.html).
|
||||
|
||||
If you follow this philosophy, you can design your API and discuss it with your clients and team before implementing a single line of code. API consumers are able to implement against the API before it's really up and running. The [api-console](https://github.com/mulesoft/api-console) provides a beautiful online documentation with "try it" features for your raml definition.
|
||||
|
||||
The RAML ecosystem provides a rich toolset for code generation (e.g. [online editor](http://rawgit.com/mulesoft/api-designer/master/dist/index.html#/?xDisableProxy=true);[ api-workbench](http://apiworkbench.com/)), automatically generated documentation, code generation (e.g. [go-raml](https://github.com/Jumpscale/go-raml)), mocking, testing and much more. We prefer RAML over Swagger because of this.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: "React.js"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
---
|
||||
|
||||
React claims to be "the V in MVC". But for us it is much more than that. React
|
||||
improved the way we approach frontend applications as we build them. Its
|
||||
functional way of writing components and its declarative JSX syntax help us to
|
||||
build interactive UIs very efficiently. React's one-way data flow keeps
|
||||
everything modular and fast and makes even large applications more readable.
|
||||
|
||||
Components are the central point of React - once we fully started
|
||||
[thinking in react](https://facebook.github.io/react/docs/thinking-in-react.html),
|
||||
our components became smaller, more reusable and better testable.
|
||||
|
||||
After some 1.5 years of experience with React and the steady growth of the
|
||||
community and ecosystem around it, we can confidently say that we still see
|
||||
great protential to build upcoming projects with React.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Redux"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
[Redux](http://redux.js.org/) helps us to maintain state in our frontend applications in a more predictable and clearer way. It is extendable though middleware, it has a great documentation and some awesome [devtools](https://github.com/gaearon/redux-devtools) that are especially helpful when you are new to Redux.
|
||||
|
||||
The functional concepts for updating the state, combined with immutable data, lead to extremely easy and enjoyable [unit tests](http://redux.js.org/docs/recipes/WritingTests.html) - this is maybe the biggest plus for us developers.
|
||||
|
||||
The official [react-redux bindings](https://github.com/reactjs/react-redux) also made it straightforward to weave Redux into our React applications. For asynchronous actions we use [redux-sagas](https://redux-saga.github.io/redux-saga/) which has proven itself as a better alternative for [redux-thunk](https://github.com/gaearon/redux-thunk).
|
||||
|
||||
Currently, we use Redux only in our React projects, but we are evaluating it together with other frameworks such as Angular or Vue.js, as well.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: "Resilience thinking"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
|
||||
Resilience is the cabability of an application or service to resist different error scenarios. Especially for distributed systems - where a lot of communication between different services happen - it's very important to explicitly think of implementing resilience.
|
||||
|
||||
There are a lot of different resilience patterns and it is also a matter of the overall software design. Typical patterns and methods used are:
|
||||
|
||||
* Do not hide API calls or any other external communication in your application (for example with unnecessary abstraction) - instead make it explicit that an external communication happens - e.g. by using the Facade Pattern. On the one hand, this makes it obvious that a potential slow and errorprone communication is going to happen, and it makes it easier to implement error handling.
|
||||
* Detect errors explicitly: Check the response message format and configure proper timeouts for external communication
|
||||
* Handle errors in a smart way: Show a nice error message to your customer or, even better, graceful degrade features - e.g. by showing some fallback text
|
||||
* Use Message-based communication where useful ([Decoupling Infrastructure via Messaging](/methods-and-patterns/decoupling-infrastructure-via-messaging.html))
|
||||
* Use Circuit Breaker to Isolate errors and allow system to recover
|
||||
* Use short activation paths in your strategic architecture - so that there is only a minimal set of communications between your services required for certain features or business requests
|
||||
|
||||
"Embrace Errors" should be the mindset - because its not a question if errors appear - it's just a question of when.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Rest Assured (Testing)"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
**[REST-assured](https://github.com/rest-assured/rest-assured)** is a Java DSL for simplifying testing of REST-based services built on top of HTTP Builder. It supports the most important http request methods and can be used to validate and verify the response of these requests.
|
||||
|
||||
At AOE, we use REST-assured with Spock to automate our API testing. We appreciate the easy-to-use DSL, which uses the Given-When-Then template (also known as Gherkin language). This template helps other project members to understand the code/test easily.
|
||||
|
||||
Because of the seamless integration with Spock and our positive experience in one of our major projects, we classify REST-assured as *assess.*
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: "RxJava"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
[RxJava](https://github.com/ReactiveX/RxJava) is the Open Source Java implementation of ReactiveX. The main concept heavily relies on the Observer- (and Subscriber)-Pattern. An Observer emits a stream of data, which can be consumed by Subscribers. The Subscriber reacts (That's where the 'Rx' comes from) asynchronously to those data events. Reactive Extensions were originally developed by Mircosoft's Erik Meijer and his team and have been ported to all major programming languages after being released to the public as Open Source software. We use RxJava (but actually RxAndroid to be precise) in the Congstar Android App to let the UI layer react to changes in the underlaying data layer.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "RxJs"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
RX/JS aka reactive streams
|
||||
|
||||
RxJS is an implementation for the reactive programming paradigm which implements mostly the observer and iterator
|
||||
pattern and follows the functional programming ideas. The pattern actually got a renaissance because it's not completely
|
||||
new but has new implementations in many frameworks and languages like Angular, Akka, Spring and many more. Reason for
|
||||
that attention actually is (in the javascript world), that observables can be cancelled (by rules too) and observables
|
||||
can pass (stream) data on multiple events. Both aspects are not well realizable using promises e.g. and both were also
|
||||
detected as a huge limitation in the JavaScript community — and so it's worth to get an understanding for reactive
|
||||
programming in general.
|
||||
|
||||
We at AOE actually use RxJS in combination with Angular and think that it's worth to dive deeper into this paradigm.
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
title: "SASS"
|
||||
ring: adopt
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
SASS (Syntactically Awesome Style-Sheets) is an extension to native CSS, which, as a preprocessor, simplifies the generation of CSS by offering features that enable developers to more efficiently write robust, better readable and maintainable CSS.
|
||||
|
||||
Core features of SASS are:
|
||||
|
||||
* Nesting of rules: CSS rules can be indented, reducing redundancy of selectors and increasing readability due to shorter selectors.
|
||||
* Use of variables: Commonly-used values such as colors can be stored in variables
|
||||
* Mixins: Often-used CSS blocks can be referenced by using mixins, which work like functions
|
||||
* Extends: CSS properties can be inherited
|
||||
* SASS files can be split into modules, which leads to smaller files and better file structures
|
||||
* Operators: Simple math calculations can be applied to CSS properties
|
||||
* Easily to integrate in nodejs-environments and build tools such s [NPM](/tools/npm.html), [Gulp](/tools/gulp.html) and [Grunt](/tools/grunt.html).
|
||||
|
||||
SASS has been widely adopted for many years and has evolved to an industry-standard backed by an active community since 2006.
|
||||
|
||||
The learning curve is very smooth as SASS is fully compatible to CSS, meaning that all features are optional: Starting with SASS is as easy as renaming .css-files to .scss in a first step and then refactoring it step-by-step with the use of SASS features.
|
||||
|
||||
At AOE, SASS has been recommended by the frontend COI and is used in nearly every current project.
|
||||
|
||||
More information:
|
||||
|
||||
* [SASS Language](http://sass-lang.com/)
|
||||
* [SASSDoc](http://sassdoc.com/)
|
||||
* [Improving Sass code quality on](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom) [theguardian.com](http://theguardian.com)
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Scala Lang"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
Besides Java, Scala is the most mature language on the Java Virtual Machine. Its unique blend of object-oriented and functional language features and rich type system with advanced type inference enables one to write concise code.
|
||||
|
||||
It is fully interoperable with Java but has a big ecosystem of tools and frameworks on its own.
|
||||
|
||||
Scala provides one of the best high-level concurrency- and async features on the language level as well as on the framework level, making it the default choice of twitter and the like.
|
||||
|
||||
At AOE, we already use Scala in various projects to create scalable backend systems (Play, Akka) or for batch processing (Spark).
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "Settings Injection"
|
||||
ring: adopt
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
While deploying applications to an environment, the application typically needs to be configured for that specific environment. Typical settings include domain names, database credentials and the location of other dependent services such as cache backends, queues or session storages.
|
||||
|
||||
These settings should not be shipped with the build package. Instead, it's the environment - this build is being deployed to - that should expose these values to application. A common way to "inject" these values is by making them available as environment variables or dynamically creating configuration files for the application. You can achieve this pattern without special tools - but this concept of settings injection also works with tools such as [Consul](/tools/consul.html), [kubernetes](/platforms-and-aoe-services/kubernetes.html) (with configMaps and secrets) or [YAD](https://github.com/AOEpeople/YAD).
|
||||
|
||||
In this manner, the build package can be independent from the environment it's being deployed to - making it easier to follow the "Build once, deploy often" CI/CD principle.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "SparkPost"
|
||||
ring: assess
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
Sparkpost is an SaaS service for E-Mail delivery and E-Mail templating that can be used to send E-Mails by calling an API.
|
||||
|
||||
In a lot of projects, it is a typical requirement that different E-Mails need to be sent and that the project stakeholders want to adjust E-Mail templates and content on a relatively regular basis.
|
||||
|
||||
Also, (mass) sending E-Mails and avoiding that they are classified as Spam is not an easy topic. That's why we decided to use E-Mail delivery services in our projects and evaluated different providers.
|
||||
|
||||
We decided to start using SparkPost because of pricing, feature set and the available reviews on the Internet. There are also other possible solutions such as SendGrid or Postmark.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: "Spock + Geb"
|
||||
ring: adopt
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
[spockframework.org](http://www.spockframework.org) - Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools and continuous integration servers. Spock is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.
|
||||
|
||||
[gebish.org](http://www.gebish.org) - Geb is a browser automation solution. It brings together the power of WebDriver, the elegance of jQuery content selection, the robustness of Page Object modelling and the expressiveness of the Groovy language. It can be used for scripting, scraping and general automation or equally as a functional/web/acceptance testing solution via integration with testing frameworks such as Spock, JUnit & TestNG.
|
||||
|
||||
At AOE, we use Spock in combination with Geb in various projects for black-box testing. Mainly, we implement our functional integration and acceptance testing automation with these frameworks, which work together seamlessly. And, we also like the convenience of extending the tests with Groovy built-ins or custom extensions.
|
||||
|
||||
Because of the successful use in two of our large projects and the wide range of opportunities within the testing domain with Spock and Geb, we classify this combo with adopt.
|
||||
|
||||
|
||||
<!--except-->
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: "Spring Boot"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
With Spring Boot you create standalone Spring Applications with minimum configuration. [Spring Boot](https://projects.spring.io/spring-boot/) rapidly gets you up and running for production.
|
||||
|
||||
With an embedded Tomcat, Jetty and Undertow you have everything you need to deploy your application out-of-the-box.
|
||||
|
||||
The Spring Cloud ecosystem also gives you a lot of extension points for developing, deploying and running cloud applications.
|
||||
|
||||
It's based on the rock-solid Spring framework and provides excellent documentation.
|
||||
|
||||
At AOE, we use Spring Boot in a microservice architecture. Together with Groovy as the implementation Language, and some other Tools (Spring Security, Cloud, HATEOAS, Data, Session) from the Spring environment, we are able to create complex and powerful applications in no time.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "Spring REST Docs"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[Spring REST Docs](https://projects.spring.io/spring-restdocs/) auto generates [Asciidoctor](http://asciidoctor.org/) snippets with the help of [Spring MVC Test](http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle#spring-mvc-test-framework) or [RestAssured](https://extranet.aoe.com/confluence/pages/viewpage.action?pageId=86937862). So you can be sure that your tests are inline with the documentation.
|
||||
|
||||
At AOE, we use [Spring REST Docs](https://projects.spring.io/spring-restdocs/) to document our Rest Services and Hal Resources. We also use it to auto generate [Wiremock](/tools/wiremock.html) Stubs, so the consumer of the service can test against the exact API of the service.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: "Strategic Domain Driven Design"
|
||||
ring: adopt
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
|
||||
Design of distributed applications need to be done wisely. Strategic Domain Driven Design is an approach for modelling large-scale applications and systems and is introduced in the last part of Eric Evans' book _**Domain Driven Design**_.
|
||||
|
||||
Domain driven design is a well-known pattern family and has been established at AOE for quite some time now. Unlike Domain Driven Design, which focuses on the tactical design in an application, strategic domain driven design is an approach that is very helpful for the high-level strategic design of an application and distributed software architecture.
|
||||
|
||||
It is a pattern familiy focused on using and defining Bounded Context and thinking explicitly of the different relationship patterns and the required "translation" of similar "concepts" between the bounded contexts. It is helpful to argue and find a good strategic architecture in alignment with the requirements, the domain and by considering Conway's Law.
|
||||
A context map and a common conceptional core help to understand and improve the overall strategic picture. Especially with the [Microservice](/methods-and-patterns/microservices.html) approach, it is important to define and connect services following the low coupling - high cohesion principles by idendifying fitting bounded contexts.
|
||||
|
||||
The following chart gives an overview of possible relationships between bounded contexts:
|
||||

|
||||
|
||||
|
||||
While we have found that this approach is especially useful in designing distributed systems and applications with [microservices](/methods-and-patterns/microservices.html), we have also extended this approach to provide guidlines for general enterprise architectures.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "Styleguide Driven Development"
|
||||
ring: trial
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
The goal of Styleguide Driven Development is to develop your application user Interface independently and reusable in a Pattern Library.\
|
||||
In the old days, the frontend was developed based on page-centric Photoshop files which made it hard to change things afterwards. With styleguide driven development you build smaller elements, which are reusable in all of your frontends.
|
||||
|
||||
You can start developing your UI components (HTML/CSS/JavaScript) very early in the production phase without having to wait for a ready-to-use development system.\
|
||||
Designers and Testers can give feedback early and you can share the documentation and code with external teams.
|
||||
|
||||
At AOE, we use [Hologram](https://trulia.github.io/hologram/) to build a living documentation right from the source files. Whenever a new UI Element is needed, a developer starts building it in the styleguide -- not in the actual application code. By writing the code for the new component, the documentation for it is created instantly. Any other developer can easily see which elements exist and how it can be used in the code.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Symfony Components"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
Symfony Components are part of the [Symfony Framework](https://symfony.com/) and they are designed as decoupled and reusable PHP components.
|
||||
|
||||
Their use cases vary from simple little helpers such as a [beautified var_dump](http://symfony.com/doc/current/components/var_dumper.html) to more complex ones such as access control, list-based [security mechanisms](http://symfony.com/doc/current/components/security.html) and an easy-to-integrate [console component](http://symfony.com/doc/current/components/console.html) to give your already existing applications some CLI capabilities. They are [used by a lot of PHP-based projects](http://symfony.com/projects) such as Typo3, Magento, Composer, PHPUnit and Doctrine, with contributions continually taking place. If you are planning the next project with PHP components, you should have a look at the [Symfony Components list](http://symfony.com/components), which includes a lot of well-designed, decoupled [Open Source pieces of PHP code](https://github.com/symfony).
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
title: "Typescript"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
[TypeScript](https://www.typescriptlang.org/) is a language that gets transpiled to native JavaScript code.
|
||||
|
||||
It offers support for the latest EcmaScript features and has strict typing and support for interfaces built in.
|
||||
|
||||
JavaScript scoping, which led into recurring workarounds such as **var self = this, myFunc.bind(this)_,_**was eliminated in TypeScript.
|
||||
|
||||
In TypeScript **this** stays **this**, which leads to more readable and understandable code from an OOP perspective.
|
||||
|
||||
TypeScript continues to be actively developed by Microsoft and is well-Integrated in today's IDEs.
|
||||
|
||||
The excellent structure and the possibilities for extension make it a good choice to consider for larger JavaScript projects.
|
||||
|
||||
Typescript was the choice for [Angular](/languages-and-frameworks/angular.html) and one can assume that it will get more traction with the success of Angular in the future.
|
||||
|
||||
There are also projects that support Typescript „code execution“ on the server such as [ts-node](https://www.npmjs.com/package/ts-node).
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "TYPO3 as a Framework"
|
||||
ring: hold
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
|
||||
We should avoid building new projects around TYPO3 by default. A lot of past projects started with CMS-only features in the beginning, and, for example, developed toward highly customized E-Commerce platforms. Instead of rearranging the architecture in a useful way, functionality was built on top of TYPO3's core and its extension framework Extbase. In the context of larger projects, this lead to deployment monoliths and the inability to integrate new technologies.
|
||||
|
||||
While in the past it was easy to kickstart a TYPO3 project with AOE's custom-tailored kickstarter, we now have a lot of knowledge and tools available to start projects with a much smarter architecture.
|
||||
This does not mean you shouldn't use TYPO3 anymore, but use it as the tool it is: a content management system.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Vue.js"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
Vue is a progressive, incrementally adoptable framework for building user interfaces maintained by Evan You. Unlike [other monolithic frameworks](http://vuejs.org/v2/guide/comparison.html), the core library is focused on the view layer only and is very easy to pick up and integrate with other libraries or existing projects. Vue is also perfectly capable of powering sophisticated single-page applications when used in combination with modern tooling and supporting libraries such as [vuex](https://vuex.vuejs.org/en/) and [vue-router](http://router.vuejs.org/en/).
|
||||
|
||||
Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. Under the hood, Vue compiles the templates into Virtual DOM render functions. Combined with the [reactivity system](http://vuejs.org/v2/guide/reactivity.html) Vue is able to intelligently figure out the minimal amount of components to re-render and apply the minimal amount of DOM manipulations when the app state changes, which provides for very high performance.
|
||||
|
||||
Applications can be split into [Single File Components](http://vuejs.org/v2/guide/single-file-components.html) - a single file containing the template (HTML), style (CSS) and functionality (JS) - which simplifies maintainability and testability of the code and promotes reusability across other projects.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: "Webpack"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[Webpack](https://webpack.js.org/) is a web bundler for JavaScript applications. Instead of writing scripts to build and bundle your app like you would with [Gulp](/tools/gulp.html), you just define what files you want to load into your bundle.
|
||||
|
||||
In the following example, we define that JavaScript files should be handled by babel-loader, excluding the files from node_modules. The logic behind the process comes from the [loader](https://webpack.js.org/concepts/loaders/). You can find the right loader in [npm](https://www.npmjs.com/search?q=loader%20webpack&page=1&ranking=optimal).
|
||||
|
||||
```
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
}
|
||||
```
|
||||
|
||||
On top of that you can use [plugins](https://webpack.js.org/plugins/) to optimize your bundle like uglifying your code or put your common libraries in a separate file.
|
||||
|
||||
Under the hood, you've got nice features such as:
|
||||
|
||||
- [tree shaking](https://webpack.js.org/guides/tree-shaking/) to just bundle the features from a library you need
|
||||
- [chunk splitting](https://webpack.js.org/guides/code-splitting/) to split your code to manage the load prioritization
|
||||
|
||||
The configuration is simple and there is excellent and extensive [documentation](https://webpack.js.org/configuration/).
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "Wiremock"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
**[WireMock](http://wiremock.org/docs/)** is an HTTP mock server - it can be used to mock APIs for testing.
|
||||
|
||||
At its core, it is a web server that can be prepared to serve canned responses to particular requests (stubbing), and that captures incoming requests so that they can be checked later (verification). It also has an assortment of other useful features including record/playback of interactions with other APIs, injection of faults and delays, simulation of stateful behavior.
|
||||
|
||||
It can be used as a library by any JVM application, or run as a standalone process either on the same host as the system under test or a remote server. All of WireMock's features are accessible via its REST (JSON) interface and its Java API. Additionally, the mock server can be configured via JSON files.
|
||||
|
||||
At AOE, we use WireMock as a standalone server to mock APIs that are outside our system context to get a stable environment for testing and rapid feedback. Besides the decoupled test and development advantages, the mocked APIs can also be used in contract-based tests. We also use embedded WireMock in functional tests to stub external services. The explicit test of faults are especially helpful in building and testing the [resilience of your application](/methods-and-patterns/resilience-thinking.html).
|
||||
|
||||
Because of the features such as flexible deployment, powerful request matching and record/payback interactions, as well as the fact that the server runs stable in our project environments, we classify WireMock as *trial*.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "Xataface"
|
||||
ring: hold
|
||||
quadrant: platforms-and-aoe-services
|
||||
|
||||
---
|
||||
|
||||
In the past, we used a custom-developed toolset with Xataface,T3Deploy and a settings migration tool as an easy way to manage TYPO3- and Magento-related configurations and to automatically create environments on our shared integration/dev-servers.
|
||||
|
||||
Today, there is no advantage or need for Xataface. Don't use it anymore
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "XMLUnit"
|
||||
ring: assess
|
||||
quadrant: tools
|
||||
|
||||
---
|
||||
[XMLUnit](http://www.xmlunit.org/) is a Java and .NET testing framework for XML documents. It is very useful for performing contract tests with SOAP interfaces or other XML-based message types.
|
||||
|
||||
Comparing strings of XML can lead to instable tests because of the changing order of elements or changed values, etc. XMLUnit provides features to address these issues. It is possible to validate against an XML Schema, use XPath queries or compare against expected outcomes. It also comes with a nice diff-engine which makes it easy to check the parts of an XML document that are important.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: "ADR"
|
||||
ring: assess
|
||||
quadrant: methods-and-patterns
|
||||
|
||||
---
|
||||
Architecture Decision Records
|
||||
|
||||
ADR is a lightweight documentation of important architecture decisions taken by the team.
|
||||
Without documentation of the architecture and the architecture decisions, new team members can only do two things:
|
||||
* either (blindy) accept what they find and see or
|
||||
* (blindy) change things
|
||||
|
||||
It goes without saying that both options aren't right.
|
||||
|
||||
Therefore, we suggest documenting the important architecture decisions. We use a simple tool such as https://github.com/npryce/adr-tools and store them in version control.
|
||||
In larger projects with many teams we also establish a regular "architecture board / COI" with regular meetings.
|
||||
Often, the architecture decisions are taken in such meetings.
|
||||
|
||||
The main purpose of this documentation is to:
|
||||
* inform new team members about the previous architecture decisions and their purpose and backgrounds
|
||||
* inform the whole team (including all people who were absent)
|
||||
* create documentation that can be used to remember things (e.g. conventions, patterns, etc.)
|
||||
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
title: "Akka Streams"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
In our backend services, we frequently encounter the task to transform data
|
||||
coming from and uploading to external sources and services.
|
||||
|
||||
Building more complex data transformation processes with Akka Actors has proven
|
||||
very difficult for us in the past.
|
||||
|
||||
Seeing this data as a stream of elements could allow handling them piece by
|
||||
piece and only keeping as much of the data in-process as can currently be
|
||||
handled.
|
||||
|
||||
[Akka Streams](http://doc.akka.io/docs/akka/current/scala/stream/index.html) is
|
||||
a [Reactive Streams](http://www.reactive-streams.org/) implementation that
|
||||
provides a very end-user friendly API for setting up streams for data
|
||||
processing that are bounded in resource usage and efficient. It uses the Akka
|
||||
Actor Framework to execute these streams in an asynchronous and parallel
|
||||
fashion exploiting today's multi-core architectures without having the user to
|
||||
interact with Actors directly. It handles things such as message resending in
|
||||
failure cases and preventing message overflow. It is also interoperable with
|
||||
other Reactive Streams implementations.
|
||||
|
||||
Our first trials with Akka Streams were promising but we haven't yet implemented
|
||||
complex services with it.
|
||||
|
||||
We will continue looking into it together with the
|
||||
[Alpakka](/languages-and-frameworks/alpakka.html) Connectors for integration
|
||||
work.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: "Alpakka"
|
||||
ring: assess
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
|
||||
When using [Akka Streams](/languages-and-frameworks/akka-streams.html) to build
|
||||
reactive data transformation services you usually need to connect to several
|
||||
different services such as FTP, S3 buckets, AMQP brokers or different databases.
|
||||
|
||||
[Alpakka](https://developer.lightbend.com/docs/alpakka/current/) provides
|
||||
integration building blocks for Akka Streams to access these services in a
|
||||
reactive fashion and contains transformations for working with XML, CSV or
|
||||
JSON structured data.
|
||||
|
||||
Combined, Akka Streams and Alpakka enable us to build small reactive
|
||||
integration services with minimal resource consumption and good performance, and
|
||||
are a good alternative to larger ESB solutions or integration tools.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Angular"
|
||||
ring: trial
|
||||
quadrant: languages-and-frameworks
|
||||
|
||||
---
|
||||
In addition to numerous major upgrades from version 2 to 5, which often needed a "hands-on" approach, a lot has happened in the Angular
|
||||
ecosystem in 2017. Specifically, the improvements in the HTTP-Client, which now requires less coding effort. Or
|
||||
the vast improvements on angular.cli such as aot (ahead of time compile) for faster rendering, fewer requests and
|
||||
much smaller builds, to just name the most important ones.
|
||||
|
||||
We have achieved particularly good results using Angular in large and medium-size projects. Actually,
|
||||
it's our framework-of-choice in our telecommunication sector teams as a single-page application framework (SPA) for microservice front
|
||||
ends.
|
||||
|
||||
The convenient scaffolding of unit- and end-to-end-tests provides a quality-driven workflow.
|
||||
Also, the module- and component architecture helps to keep the codebase understandable end maintainable.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user