The Definitive Framework for Technology Selection in Legacy-Industry DX: From a Real Example in the Lumber-Distribution Industry
Introduction: why legacy-industry DX is hard
Manufacturing, construction, logistics, agriculture/forestry/fisheries — these "legacy industries" still run on phone, fax, and Excel as the norm. Unable to receive the benefits of digitalization, they struggle with inefficient workflows, knowledge siloed in individuals, and labor shortages.
Over the past two years I took on the DX of the lumber-distribution industry, a "super-legacy industry," and developed a product that won the METI Minister's Award. In this article, I present the "definitive framework for technology selection in legacy-industry DX" derived from that experience.
In particular, I focus on the decision-making of technology selection — "which technology should you choose" — and provide practical guidelines.
Framework overview: 5 evaluation axes
In technology selection for legacy-industry DX, we select technology on the following 5 evaluation axes.
| Evaluation axis | Importance | Description |
|---|---|---|
| Industry fit | ★★★★★ | Can it handle the industry-specific complexity (commerce flow, user attributes, workflows)? |
| Non-engineer readiness | ★★★★☆ | Can on-site non-engineers master it (UI/UX, manuals)? |
| Phased rollout | ★★★★☆ | Can it migrate gradually while coexisting with existing workflows? |
| Long-term maintainability | ★★★★★ | Can it endure 10+ years of long-term operation (technical debt, siloing risk)? |
| Cost optimization | ★★★☆☆ | Are the initial and running costs appropriate? |
Case study: technology selection for lumber-distribution-industry DX
Industry characteristics: 8 kinds of stakeholders and a complex commerce flow
In the lumber-distribution industry, the following 8 kinds of stakeholders exist.
Forestry → Market → Sawmill → Precut → Builder
↓
Manufacturer → Wholesaler → Other
The following differ per stakeholder:
- Executable features (ordering, inventory management, quote creation)
- Viewable information (own inventory only, the market's entire inventory, a partner's inventory)
- Pricing authority (market price, wholesale price, retail price)
The technology-selection decision process
Axis 1: Industry fit — AWS Cognito + custom logic
Requirements:
- Authentication/authorization per 8 kinds of user attributes
- Different permission management for each attribute
Comparison of candidate technologies:
| Technology | Pros | Cons | Verdict |
|---|---|---|---|
| Firebase Auth | Easy, large free tier | Low flexibility of custom attributes | × |
| Auth0 | High flexibility | High cost (MAU billing) | △ |
| AWS Cognito | AWS integration, custom attributes | Learning cost | ◎ |
| In-house implementation | Full customization | Security risk, large effort | × |
Decision: AWS Cognito
Reason:
// AWS Cognitoのカスタム属性で8種類のユーザー属性を管理
interface UserAttributes {
'custom:user_type': 'lumber_mill' | 'market' | 'manufacturer' | 'precut' | 'builder' | 'wholesaler' | 'other';
'custom:company_id': string;
'custom:permissions': string; // "create_order,view_inventory,manage_users"
}
// 権限チェック関数
const hasPermission = (user: UserAttributes, permission: string): boolean => {
const permissions = user['custom:permissions'].split(',');
return permissions.includes(permission);
};
Axis 2: Frontend — React + TypeScript + Vite
Requirements:
- UI control per complex user attribute
- Large-volume data operations (inventory list, order history)
- Fast response
Comparison of candidate technologies:
| Technology | Pros | Cons | Verdict |
|---|---|---|---|
| jQuery | Low learning cost | Hard to manage complex UI | × |
| Vue.js | Simple, easy to learn | Unsuited to large-scale apps | △ |
| React + TypeScript | Type-safe, ecosystem | Learning cost | ◎ |
| Next.js | SSR/SSG optimization | Over-spec (SPA-oriented) | △ |
Decision: React + TypeScript + Vite
Reason:
// TypeScriptによる型安全なユーザー属性管理
type UserType = 'lumber_mill' | 'market' | 'manufacturer';
interface User {
id: string;
userType: UserType;
companyId: string;
permissions: Set<Permission>;
}
// ユーザー属性ごとの条件付きレンダリング
const Dashboard: React.FC<{ user: User }> = ({ user }) => {
return (
<div>
{user.permissions.has('create_order') && <CreateOrderButton />}
{user.permissions.has('view_inventory') && <InventoryList />}
{user.userType === 'market' && <MarketPriceChart />}
</div>
);
};
Introducing Tanstack Query (React Query):
import { useQuery } from '@tanstack/react-query';
const useInventory = (companyId: string) => {
return useQuery({
queryKey: ['inventory', companyId],
queryFn: () => fetchInventory(companyId),
staleTime: 5 * 60 * 1000, // 5分間キャッシュ
refetchOnWindowFocus: true,
});
};
// コンポーネントでの使用
const InventoryList: React.FC = () => {
const { data, isLoading, error } = useInventory(user.companyId);
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <Table data={data} />;
};
Pros:
- Efficient API caching (reduces wasteful requests)
- Separation of concerns for data fetching (separating components and logic)
Axis 3: Backend — Python (Flask + SQLAlchemy)
Requirements:
- Heavy PDF/Excel batch processing (parallel processing)
- Complex business logic (price calculation, inventory management)
- Turning existing Excel data into a DB
Comparison of candidate technologies:
| Technology | Pros | Cons | Verdict |
|---|---|---|---|
| Node.js (Express) | TypeScript unification, lightweight | Unsuited to heavy processing | △ |
| Python (Flask) | Data processing, parallel processing | Performance (GIL) | ◎ |
| Golang (Gin) | Fast, concurrent processing | Small ecosystem | △ |
| Java (Spring Boot) | Enterprise track record | Heavy, learning cost | × |
Decision: Python (Flask + SQLAlchemy)
Reason:
# 帳票生成:openpyxl で Excel を作り、LibreOffice ヘッドレスで PDF 化
import subprocess
from openpyxl import load_workbook
def generate_invoice(order) -> tuple[str, str]:
"""請求書を Excel テンプレートから生成し、PDF へ変換する。"""
wb = load_workbook("templates/invoice.xlsx")
ws = wb.active
ws["B2"] = order.invoice_number
ws["B3"] = order.customer_name
# ... 明細を流し込む
xlsx_path = f"invoices/{order.id}.xlsx"
wb.save(xlsx_path)
# 業務帳票はレイアウト再現性が重要なので、表計算をそのまま PDF 化する
subprocess.run(
["libreoffice", "--headless", "--convert-to", "pdf", "--outdir", "invoices/", xlsx_path],
check=True,
)
return xlsx_path, xlsx_path.replace(".xlsx", ".pdf")
# Excel→DB 取り込み:openpyxl(read_only)+ psycopg2 の execute_values で一括 INSERT
from openpyxl import load_workbook
from psycopg2.extras import execute_values
def import_excel_to_db(conn, file_path: str) -> None:
"""既存 Excel を DB へ一括インポートする(S3 トリガーの Lambda で実行)。"""
wb = load_workbook(file_path, read_only=True, data_only=True)
rows = []
for r in wb.active.iter_rows(min_row=2, values_only=True):
product_id, name, price, stock = r
if product_id is None or price is None:
continue # 欠損行はスキップ
rows.append((product_id, name, float(price), stock))
with conn.cursor() as cur:
execute_values(
cur,
"INSERT INTO products (product_id, name, price, stock) VALUES %s",
rows, # 行ループの個別 INSERT ではなく一括投入(高速・低負荷)
)
conn.commit()
Pros:
- openpyxl: edit the template Excel as-is, faithfully reproducing the industry's document layouts
- LibreOffice headless conversion: generate a PDF identical to the Excel down to the millimeter (for business documents, a matching appearance matters)
- Bulk INSERT via execute_values: orders of magnitude faster than per-row ORM add; the import is separated into an S3-event-driven Lambda so it doesn't strain the server proper
Axis 4: Infrastructure — AWS (ECS on Fargate + Terraform)
Requirements:
- A scalable SaaS foundation
- Highly reproducible infrastructure (IaC)
- Container-based microservices
Comparison of candidate technologies:
| Technology | Pros | Cons | Verdict |
|---|---|---|---|
| AWS EC2 | High freedom | Manual scaling, management cost | × |
| AWS ECS on Fargate | Serverless, auto-scale | Slightly higher cost | ◎ |
| Kubernetes (EKS) | Highest flexibility | Complex, over-spec | △ |
| Heroku | Easy | High cost, low flexibility | × |
Decision: AWS ECS on Fargate + Terraform
Codifying infrastructure with Terraform:
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "lumber-saas-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
}
# ECS Task Definition
resource "aws_ecs_task_definition" "app" {
family = "lumber-saas-app"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "1024"
memory = "2048"
container_definitions = jsonencode([{
name = "app"
image = "${var.ecr_repository_url}:latest"
portMappings = [{
containerPort = 8080
protocol = "tcp"
}]
environment = [
{ name = "DATABASE_URL", value = var.database_url },
{ name = "COGNITO_USER_POOL_ID", value = aws_cognito_user_pool.main.id }
]
}])
}
# Auto Scaling
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = 10
min_capacity = 2
resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.app.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
resource "aws_appautoscaling_policy" "ecs_policy" {
name = "scale-up"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
target_tracking_scaling_policy_configuration {
target_value = 70.0
predefined_metric_specification {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
}
}
Pros:
- Fargate: no server management, auto-scale
- Terraform: codify the entire infrastructure, 100% reproducibility
- Auto Scaling: auto-scale according to CPU utilization (cost optimization)
Non-engineer readiness: thorough simplification of UI/UX
The challenge: can on-site non-engineers use it?
In legacy-industry workplaces, there are many users with low IT literacy. You need to accommodate users who "can use Excel but can't use a web system."
Implementation: intuitive UI design
1. Excel-like operability
// ag-Grid を使用したExcelライクなテーブル
import { AgGridReact } from 'ag-grid-react';
const InventoryTable: React.FC = () => {
const columnDefs = [
{ field: 'product_name', headerName: '商品名', editable: true },
{ field: 'stock', headerName: '在庫数', editable: true },
{ field: 'price', headerName: '単価', editable: true },
];
const onCellValueChanged = (params: any) => {
// セル編集時、自動保存
updateInventory(params.data);
};
return (
<AgGridReact
columnDefs={columnDefs}
rowData={inventoryData}
onCellValueChanged={onCellValueChanged}
enableRangeSelection={true} // Excel風の範囲選択
/>
);
};
2. Clear error messages
// ❌ 悪い例
throw new Error('ValidationError: field "price" must be positive');
// ✅ 良い例
throw new Error('価格は0円以上で入力してください。現在の入力値: -1000円');
Phased rollout: coexistence with existing operations
The challenge: "a sudden full migration" fails
In legacy industries, "suddenly abolishing Excel and migrating to a web system" confuses the field and fails.
Strategy: 3-stage rollout
Phase 1 (1–3 months): Information sharing only (Excel allowed alongside)
↓
Phase 2 (4–6 months): Partial migration of the ordering feature (Excel alongside)
↓
Phase 3 (7–12 months): Full migration (Excel retired)
Implementation example:
# Phase 1: Excel アップロード機能
@app.route('/api/inventory/upload', methods=['POST'])
def upload_excel():
"""既存Excelをアップロードして、DBに同期"""
file = request.files['file']
df = pd.read_excel(file)
# DB同期
sync_inventory_from_dataframe(df)
return jsonify({'message': 'Excelをアップロードしました。Web画面でも確認できます。'})
# Phase 2: Excelダウンロード機能(Excel併用)
@app.route('/api/inventory/download', methods=['GET'])
def download_excel():
"""Web上のデータをExcelでダウンロード"""
inventory = Inventory.query.all()
df = pd.DataFrame([inv.to_dict() for inv in inventory])
output = io.BytesIO()
df.to_excel(output, index=False)
output.seek(0)
return send_file(output, mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True, download_name='inventory.xlsx')
Summary: the 5 principles of legacy-industry-DX technology selection
- Prioritize industry fit above all — choose technology that can handle the industry-specific complexity, not a generic solution
- Be thorough about non-engineer readiness — design an intuitive UI/UX that even users with low IT literacy can use
- Plan a phased rollout — don't migrate all at once; migrate gradually while coexisting with existing operations
- Secure long-term maintainability — a design that endures 10+ years of long-term operation without breeding technical debt
- Guarantee reproducibility with IaC — codify infrastructure to handle disaster recovery and scale-out
Your legacy-industry DX is achievable too
Manufacturing, construction, logistics, agriculture/forestry/fisheries — we support the DX of every legacy industry. From requirements definition through design, implementation, and infrastructure construction, we can handle it one-stop.
We offer a free technical consultation (30 minutes), so feel free to get in touch.