Introduction

Service-Oriented Architecture (SOA) is a powerful approach to designing modular, scalable applications. In this post, we will develop an airline booking system using SOA in C# with .NET 9. We will incorporate modern architecture patterns, including the Resolver, Repository, and Service patterns, along with Entity Framework for data access and Data Transfer Objects (DTOs) for encapsulation. We will also implement Dependency Injection (DI) to ensure better separation of concerns and easier testing. Finally, we will bind the data to an MVVM-oriented simple UI, leveraging asynchronous programming where necessary, and making use of cancellation tokens for better request management. To ensure the UI displays meaningful data, we will include sample data generation.

Architecture Overview

The system will be composed of the following layers:

  • Presentation Layer (MVVM-based UI)
  • Service Layer (Business logic and orchestration)
  • Repository Layer (Data access with Entity Framework Core)
  • Database Layer (SQL Server using EF Core)
  • Dependency Injection (Managing dependencies efficiently)

Setting Up the Project

We will create a C# .NET 9 solution with multiple projects:

  1. AirlineBooking.Domain – Contains entity models and DTOs
  2. AirlineBooking.Data – Implements the repository pattern using Entity Framework Core
  3. AirlineBooking.Service – Implements business logic
  4. AirlineBooking.API – Exposes services via Web API
  5. AirlineBooking.UI – A simple WPF UI using MVVM

1. Defining the Domain Entities and DTOs

First, let’s define the core entities and DTOs.

public class Flight
{
    public int Id { get; set; }
    public string FlightNumber { get; set; } = string.Empty;
    public string Origin { get; set; } = string.Empty;
    public string Destination { get; set; } = string.Empty;
    public DateTime DepartureTime { get; set; }
    public int AvailableSeats { get; set; }
}

public class FlightDto
{
    public int Id { get; set; }
    public string FlightNumber { get; set; } = string.Empty;
    public string Origin { get; set; } = string.Empty;
    public string Destination { get; set; } = string.Empty;
}

2. Implementing the Repository Pattern

The repository pattern abstracts data access logic.

public interface IFlightRepository
{
    Task<IEnumerable<Flight>> GetFlightsAsync(CancellationToken cancellationToken);
}

public class FlightRepository : IFlightRepository
{
    public async Task<IEnumerable<Flight>> GetFlightsAsync(CancellationToken cancellationToken)
    {
        await Task.Delay(500, cancellationToken); // Simulate database latency
        return new List<Flight>
        {
            new Flight { Id = 1, FlightNumber = "LH123", Origin = "Munich", Destination = "New York", DepartureTime = DateTime.UtcNow.AddHours(5), AvailableSeats = 20 },
            new Flight { Id = 2, FlightNumber = "BA456", Origin = "London", Destination = "Berlin", DepartureTime = DateTime.UtcNow.AddHours(3), AvailableSeats = 15 }
        };
    }
}

3. Implementing the Service Layer

public interface IFlightService
{
    Task<IEnumerable<FlightDto>> GetFlightsAsync(CancellationToken cancellationToken);
}

public class FlightService : IFlightService
{
    private readonly IFlightRepository _repository;
    
    public FlightService(IFlightRepository repository)
    {
        _repository = repository;
    }
    
    public async Task<IEnumerable<FlightDto>> GetFlightsAsync(CancellationToken cancellationToken)
    {
        var flights = await _repository.GetFlightsAsync(cancellationToken);
        return flights.Select(f => new FlightDto
        {
            Id = f.Id,
            FlightNumber = f.FlightNumber,
            Origin = f.Origin,
            Destination = f.Destination
        });
    }
}

4. Implementing the MVVM-Based WPF UI

FlightViewModel

public class FlightViewModel : INotifyPropertyChanged
{
    private readonly IFlightService _flightService;
    private ObservableCollection<FlightDto> _flights;
    
    public ObservableCollection<FlightDto> Flights
    {
        get => _flights;
        set { _flights = value; OnPropertyChanged(); }
    }

    public ICommand LoadFlightsCommand { get; }

    public FlightViewModel(IFlightService flightService)
    {
        _flightService = flightService;
        Flights = new ObservableCollection<FlightDto>();
        LoadFlightsCommand = new AsyncRelayCommand(LoadFlightsAsync);
    }
    
    private async Task LoadFlightsAsync()
    {
        using var cts = new CancellationTokenSource();
        var flights = await _flightService.GetFlightsAsync(cts.Token);
        App.Current.Dispatcher.Invoke(() =>
        {
            Flights.Clear();
            foreach (var flight in flights)
                Flights.Add(flight);
        });
    }
    
    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null!)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

FlightView.xaml

<Window x:Class="AirlineBooking.UI.Views.FlightView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Flight Booking" Height="350" Width="600">
    <Grid>
        <ListView ItemsSource="{Binding Flights}" Margin="10">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Flight No" DisplayMemberBinding="{Binding FlightNumber}"/>
                    <GridViewColumn Header="Origin" DisplayMemberBinding="{Binding Origin}"/>
                    <GridViewColumn Header="Destination" DisplayMemberBinding="{Binding Destination}"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Button Content="Load Flights" Command="{Binding LoadFlightsCommand}" HorizontalAlignment="Right" Margin="10"/>
    </Grid>
</Window>

Conclusion

We built an SOA-based airline booking system with a complete MVVM-based WPF UI including XAML layout. Sample data ensures the UI displays meaningful flights without requiring a full database. This structure ensures modularity, scalability, and ease of maintenance.

Building a SOA-Based Airline Booking System in C# with Modern Architecture Patterns (Using .NET 9)

Johannes Rest


.NET Architekt und Entwickler


Beitragsnavigation


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert