【Webシステムと業務システムの違いとは?SIerが理解すべき設計のポイント】

SIer Tech Blog
2025年3月21日
【Webシステムと業務システムの違いとは?SIerが理解すべき設計のポイント】
SIerとして働く中で、Webシステムと業務システムの両方に携わる機会が増えています。本記事では、これら2つのシステムタイプの違いと、それぞれの設計アプローチについて解説します。
1. Webシステムと業務システムの基本的な違い
1.1 特徴の比較
以下の表で、WebシステムとSIerが得意とする業務システムの主な違いを比較します:
観点 | Webシステム | 業務システム |
---|---|---|
主な利用者 | 一般ユーザー | 企業の従業員 |
利用規模 | 不特定多数 | 特定のユーザー群 |
アクセスパターン | 予測が難しい | 比較的予測可能 |
セキュリティ要件 | 外部攻撃への対応が重要 | 内部統制が重要 |
可用性要件 | 24/365が基本 | 業務時間内が中心 |
UI/UX要件 | 直感的な操作性が重要 | 業務効率が重要 |
変更頻度 | 頻繁な更新 | 計画的な更新 |
1.2 システムの目的と価値
1.2.1 Webシステムの目的
- ユーザー獲得: 多くのユーザーを集める
- サービス提供: オンラインでのサービス展開
- マーケティング: ブランド構築やプロモーション
- 収益化: 広告収入やサービス利用料
1.2.2 業務システムの目的
- 業務効率化: 作業の自動化と効率化
- コスト削減: 人的コストの削減
- 品質向上: ヒューマンエラーの防止
- 内部統制: コンプライアンスの確保
2. アーキテクチャ設計の違い
2.1 Webシステムのアーキテクチャ
// Webシステムの典型的なアーキテクチャ例
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public ResponseEntity<List<Product>> getProducts(
@RequestParam(required = false) String category,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
List<Product> products = productService.findProducts(category, page, size);
return ResponseEntity.ok(products);
}
@PostMapping
public ResponseEntity<Product> createProduct(@RequestBody ProductRequest request) {
Product product = productService.createProduct(request);
return ResponseEntity
.created(URI.create("/api/products/" + product.getId()))
.body(product);
}
}
// スケーラブルなサービス層
@Service
public class ProductService {
private final ProductRepository productRepository;
private final CacheManager cacheManager;
private final EventPublisher eventPublisher;
@Cacheable(value = "products", key = "#category + '-' + #page + '-' + #size")
public List<Product> findProducts(String category, int page, int size) {
return productRepository.findByCategory(category, PageRequest.of(page, size));
}
@Transactional
public Product createProduct(ProductRequest request) {
Product product = new Product(request);
product = productRepository.save(product);
// イベント発行(非同期処理のトリガー)
eventPublisher.publish(new ProductCreatedEvent(product));
return product;
}
}
2.2 業務システムのアーキテクチャ
// 業務システムの典型的なアーキテクチャ例
@Controller
@RequestMapping("/sales")
public class SalesController {
private final SalesService salesService;
private final AuthorizationService authorizationService;
@Autowired
public SalesController(
SalesService salesService,
AuthorizationService authorizationService) {
this.salesService = salesService;
this.authorizationService = authorizationService;
}
@PostMapping("/orders")
public String createOrder(
@ModelAttribute OrderForm form,
@AuthenticationPrincipal User user,
Model model) {
// 権限チェック
if (!authorizationService.canCreateOrder(user)) {
throw new UnauthorizedException("注文作成権限がありません");
}
try {
// 業務ロジックの実行
OrderResult result = salesService.createOrder(form, user);
// 監査ログの記録
AuditLogger.log(
"ORDER_CREATED",
user.getId(),
result.getOrderId()
);
model.addAttribute("result", result);
return "sales/orderComplete";
} catch (BusinessException e) {
model.addAttribute("error", e.getMessage());
return "sales/orderForm";
}
}
}
// トランザクション管理を重視したサービス層
@Service
public class SalesService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
private final ApprovalService approvalService;
@Transactional
public OrderResult createOrder(OrderForm form, User user) {
// 入力値の業務バリデーション
validateBusinessRules(form);
// 在庫チェック
inventoryService.checkAndReserveStock(form.getItems());
// 注文データの作成
Order order = new Order(form);
order.setCreatedBy(user);
order = orderRepository.save(order);
// 承認ワークフローの開始
approvalService.startApprovalFlow(order);
return new OrderResult(order);
}
private void validateBusinessRules(OrderForm form) {
// 業務ルールに基づくバリデーション
if (form.getAmount().compareTo(new BigDecimal("1000000")) >= 0) {
throw new BusinessException("100万円以上の注文は特別承認が必要です");
}
// その他の業務ルールチェック
}
}
3. 非機能要件の違い
3.1 パフォーマンス要件
3.1.1 Webシステムのパフォーマンス
// Webシステムのパフォーマンス最適化例
@Configuration
@EnableCaching
public class WebPerformanceConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.maxAge(3600);
}
};
}
}
// キャッシュを活用したサービス
@Service
public class ProductCatalogService {
private final ProductRepository repository;
@Cacheable(value = "products", key = "#category")
public List<Product> getProductsByCategory(String category) {
return repository.findByCategory(category);
}
@CacheEvict(value = "products", key = "#product.category")
public void updateProduct(Product product) {
repository.save(product);
}
}
3.1.2 業務システムのパフォーマンス
// 業務システムのパフォーマンス最適化例
@Configuration
public class BusinessPerformanceConfig {
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
@Override
public PlatformTransactionManager getTransactionManager() {
return new DataSourceTransactionManager(dataSource());
}
};
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
return executor;
}
}
// バッチ処理の最適化
@Service
public class ReportGenerationService {
private final TaskExecutor taskExecutor;
@Async
public Future<ReportResult> generateReport(ReportRequest request) {
return new AsyncResult<>(processReport(request));
}
private ReportResult processReport(ReportRequest request) {
// 大量データの効率的な処理
try (Stream<Data> dataStream = repository.streamAll()) {
return dataStream
.filter(this::isRelevant)
.map(this::processData)
.collect(toReportResult());
}
}
}
3.2 セキュリティ要件
3.2.1 Webシステムのセキュリティ
// Webシステムのセキュリティ設定例
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.headers()
.contentSecurityPolicy("default-src 'self'")
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
// セキュアなAPIエンドポイント
@RestController
@RequestMapping("/api")
public class SecureApiController {
@PostMapping("/data")
public ResponseEntity<?> processData(
@RequestBody @Valid DataRequest request,
@AuthenticationPrincipal Jwt jwt) {
// 入力値の無害化
String sanitizedInput = Jsoup.clean(request.getData(), Safelist.basic());
// 処理の実行
return ResponseEntity.ok(processSecureData(sanitizedInput));
}
}
3.2.2 業務システムのセキュリティ
// 業務システムのセキュリティ設定例
@Configuration
@EnableWebSecurity
public class BusinessSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.headers()
.frameOptions().deny()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login")
.maximumSessions(1)
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/manager/**").hasRole("MANAGER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.and()
.rememberMe()
.key("uniqueAndSecret")
.tokenValiditySeconds(86400);
}
}
// 権限管理とアクセス制御
@Service
public class BusinessAuthorizationService {
private final RoleHierarchy roleHierarchy;
private final AuditLogger auditLogger;
public boolean hasPermission(User user, String operation, String resource) {
boolean hasPermission = checkPermission(user, operation, resource);
// 監査ログの記録
auditLogger.logAccessCheck(
user.getId(),
operation,
resource,
hasPermission
);
return hasPermission;
}
@PreAuthorize("hasRole('ADMIN') or hasRole('MANAGER')")
public void performSensitiveOperation(SensitiveData data) {
// 機密データの処理
}
}
4. データモデリングの違い
4.1 Webシステムのデータモデル
// Webシステムのデータモデル例
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(length = 1000)
private String description;
@Column(nullable = false)
private BigDecimal price;
@ElementCollection
private Set<String> tags = new HashSet<>();
@Version
private Long version;
// SEO対策用のメタデータ
@Embedded
private SeoMetadata seoMetadata;
}
// NoSQLデータの活用
@Document(collection = "product_views")
public class ProductView {
@Id
private String id;
private Long productId;
private Long viewCount;
private List<UserInteraction> recentInteractions;
// アナリティクス用のデータ
private Map<String, Object> analytics;
}
4.2 業務システムのデータモデル
// 業務システムのデータモデル例
@Entity
@Table(name = "orders")
@EntityListeners(AuditingEntityListener.class)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String orderNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items = new ArrayList<>();
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OrderStatus status;
@Column(nullable = false)
private BigDecimal totalAmount;
// 監査情報
@CreatedBy
private String createdBy;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private LocalDateTime lastModifiedAt;
// 承認情報
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Approval> approvals = new ArrayList<>();
// ビジネスロジック
public void approve(User approver) {
if (!canBeApproved()) {
throw new BusinessException("この注文は承認できません");
}
Approval approval = new Approval(this, approver);
approvals.add(approval);
if (isFullyApproved()) {
this.status = OrderStatus.APPROVED;
}
}
private boolean canBeApproved() {
return status == OrderStatus.PENDING_APPROVAL;
}
private boolean isFullyApproved() {
return approvals.size() >= getRequiredApprovalCount();
}
private int getRequiredApprovalCount() {
return totalAmount.compareTo(new BigDecimal("1000000")) >= 0 ? 2 : 1;
}
}
5. 開発プロセスの違い
5.1 Webシステムの開発プロセス
- アジャイル開発:迅速な機能リリース
- 継続的デリバリー:自動化されたデプロイ
- A/Bテスト:ユーザー反応の測定
- フィードバックループ:分析に基づく改善
// フィーチャーフラグの実装例
@Service
public class FeatureToggleService {
private final FeatureRepository repository;
public boolean isFeatureEnabled(String featureName, User user) {
Feature feature = repository.findByName(featureName);
if (feature == null || !feature.isActive()) {
return false;
}
// ユーザーセグメントに基づく判定
return feature.isEnabledForUser(user);
}
}
// A/Bテストの実装
@Service
public class AbTestService {
private final ExperimentRepository repository;
private final AnalyticsService analytics;
public Variant getVariant(String experimentId, User user) {
Experiment experiment = repository.findById(experimentId);
Variant variant = experiment.getVariantForUser(user);
// 実験データの記録
analytics.trackExperiment(experimentId, user.getId(), variant);
return variant;
}
}
5.2 業務システムの開発プロセス
- ウォーターフォール型:計画的な開発
- 要件定義の重視:詳細な仕様書作成
- 品質管理:厳密なテスト工程
- 計画的なリリース:慎重な本番展開
// 要件トレーサビリティの実装例
@Service
public class RequirementTraceService {
private final RequirementRepository repository;
private final TestCaseRepository testCaseRepository;
public RequirementTrace getTraceability(String requirementId) {
Requirement requirement = repository.findById(requirementId);
List<TestCase> testCases = testCaseRepository.findByRequirementId(requirementId);
return new RequirementTrace(requirement, testCases);
}
}
// テスト実行管理
@Service
public class TestExecutionService {
private final TestSuiteRepository repository;
private final TestResultRepository resultRepository;
@Transactional
public TestResult executeTestSuite(String suiteId) {
TestSuite suite = repository.findById(suiteId);
TestResult result = new TestResult();
for (TestCase testCase : suite.getTestCases()) {
TestCaseResult caseResult = executeTestCase(testCase);
result.addCaseResult(caseResult);
}
return resultRepository.save(result);
}
}
6. 運用管理の違い
6.1 Webシステムの運用管理
// モニタリングの実装例
@Configuration
public class WebMonitoringConfig {
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
@Bean
public WebMvcConfigurer metricsConfigurer(MeterRegistry registry) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MetricsInterceptor(registry));
}
};
}
}
// パフォーマンスモニタリング
@Component
public class PerformanceMonitor {
private final MeterRegistry registry;
public void recordResponseTime(String endpoint, long timeMs) {
registry.timer("http.server.requests",
"endpoint", endpoint)
.record(timeMs, TimeUnit.MILLISECONDS);
}
public void recordErrorRate(String endpoint, String errorType) {
registry.counter("http.server.errors",
"endpoint", endpoint,
"error", errorType)
.increment();
}
}
6.2 業務システムの運用管理
// バッチジョブ管理の実装例
@Configuration
public class BatchJobConfig {
@Bean
public JobRepository jobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
return factory.getObject();
}
@Bean
public SimpleJobLauncher jobLauncher() throws Exception {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(jobRepository());
return launcher;
}
}
// システム運用監視
@Service
public class SystemOperationMonitor {
private final AlertService alertService;
private final MaintenanceScheduler scheduler;
public void checkSystemHealth() {
// データベース接続の確認
checkDatabaseConnection();
// バッチジョブの状態確認
checkBatchJobStatus();
// ディスク使用率の確認
checkDiskUsage();
// メモリ使用率の確認
checkMemoryUsage();
}
private void checkDatabaseConnection() {
try {
dataSource.getConnection().close();
} catch (SQLException e) {
alertService.sendAlert(
AlertLevel.CRITICAL,
"データベース接続エラー",
e.getMessage()
);
}
}
}
7. まとめ
WebシステムとSIerが得意とする業務システムには、それぞれ以下のような特徴があります:
7.1 Webシステムの特徴
-
柔軟性と拡張性
- 急激な負荷変動への対応
- 新機能の迅速な追加
- クラウドリソースの活用
-
ユーザー体験重視
- 直感的なUI/UX
- レスポンシブデザイン
- パフォーマンスの最適化
-
セキュリティ対策
- 外部攻撃への防御
- データ保護
- コンプライアンス対応
7.2 業務システムの特徴
-
信頼性と安定性
- 堅牢な処理
- データの整合性
- 障害時の回復性
-
業務プロセスの最適化
- ワークフロー管理
- 承認プロセス
- 監査証跡
-
内部統制
- アクセス制御
- 操作ログ
- コンプライアンス
7.3 設計アプローチの選択
システムの特性に応じて、適切な設計アプローチを選択することが重要です:
-
要件の明確化
- ビジネス目標の理解
- ユーザーニーズの把握
- 制約条件の特定
-
アーキテクチャの選択
- スケーラビリティ要件
- セキュリティ要件
- 運用要件
-
技術選定
- フレームワークの選択
- データベースの選択
- インフラストラクチャの選択
参考文献
- 「エンタープライズアプリケーションアーキテクチャパターン」- Martin Fowler
- 「Webを支える技術」- 山本陽平
- 「業務システム設計の実践」- 技術評論社
- 「システム設計の原則」- Robert C. Martin
WebシステムとSIerが得意とする業務システムは、それぞれ異なる特徴と要件を持っています。システムの目的と要件を十分に理解し、適切な設計アプローチを選択することで、効果的なシステム開発が可能となります。
関連記事
2025/3/25
【「動作保証はどこまで?」SIerのためのシステム保守の基本】
SIerエンジニアのためのシステム保守ガイド。業務システムの保守範囲の定義から具体的な保守活動まで、実践的なアプローチを解説します。
2025/3/24
【SIerが知るべきログ設計のベストプラクティス】
SIerエンジニアのためのログ設計ガイド。業務システムにおける効果的なログ設計から運用管理まで、実践的なベストプラクティスを解説します。
2025/3/23
【長年運用されている業務システムの"負債"とどう向き合うか?】
SIerエンジニアのための技術的負債管理ガイド。長年運用されてきた業務システムの負債を理解し、効果的に管理・改善していくための実践的なアプローチを解説します。