森 宝松 SIer Tech Blog

【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システムの特徴

  1. 柔軟性と拡張性

    • 急激な負荷変動への対応
    • 新機能の迅速な追加
    • クラウドリソースの活用
  2. ユーザー体験重視

    • 直感的なUI/UX
    • レスポンシブデザイン
    • パフォーマンスの最適化
  3. セキュリティ対策

    • 外部攻撃への防御
    • データ保護
    • コンプライアンス対応

7.2 業務システムの特徴

  1. 信頼性と安定性

    • 堅牢な処理
    • データの整合性
    • 障害時の回復性
  2. 業務プロセスの最適化

    • ワークフロー管理
    • 承認プロセス
    • 監査証跡
  3. 内部統制

    • アクセス制御
    • 操作ログ
    • コンプライアンス

7.3 設計アプローチの選択

システムの特性に応じて、適切な設計アプローチを選択することが重要です:

  1. 要件の明確化

    • ビジネス目標の理解
    • ユーザーニーズの把握
    • 制約条件の特定
  2. アーキテクチャの選択

    • スケーラビリティ要件
    • セキュリティ要件
    • 運用要件
  3. 技術選定

    • フレームワークの選択
    • データベースの選択
    • インフラストラクチャの選択

参考文献

  1. 「エンタープライズアプリケーションアーキテクチャパターン」- Martin Fowler
  2. 「Webを支える技術」- 山本陽平
  3. 「業務システム設計の実践」- 技術評論社
  4. 「システム設計の原則」- Robert C. Martin

WebシステムとSIerが得意とする業務システムは、それぞれ異なる特徴と要件を持っています。システムの目的と要件を十分に理解し、適切な設計アプローチを選択することで、効果的なシステム開発が可能となります。

関連記事

2025/3/25

【「動作保証はどこまで?」SIerのためのシステム保守の基本】

SIerエンジニアのためのシステム保守ガイド。業務システムの保守範囲の定義から具体的な保守活動まで、実践的なアプローチを解説します。

2025/3/24

【SIerが知るべきログ設計のベストプラクティス】

SIerエンジニアのためのログ設計ガイド。業務システムにおける効果的なログ設計から運用管理まで、実践的なベストプラクティスを解説します。

2025/3/23

【長年運用されている業務システムの"負債"とどう向き合うか?】

SIerエンジニアのための技術的負債管理ガイド。長年運用されてきた業務システムの負債を理解し、効果的に管理・改善していくための実践的なアプローチを解説します。