මාතෘකාව 5: බැංකු පද්ධතිය ගොඩනැගීම - මූලික Logic එක

අත්තිවාරම සූදානම්. දැන් අපි අපගේ යෙදුම සැබෑ බැංකු පද්ධතියක් බවට පත් කරන විශේෂාංග ගොඩනගමු.

CRUD සිට Business Logic දක්වා

පසුගිය මාතෘකාවේදී, අපි මූලික CRUD (Create, Read, Update, Delete) මෙහෙයුම් ඉගෙන ගත්තෙමු. දැන්, අපි Business Logic කෙරෙහි අවධානය යොමු කරමු. මෙයට නීති යෙදීම, ගණනය කිරීම් සිදු කිරීම සහ බහුවිධ පියවර සම්බන්ධීකරණය කිරීම ඇතුළත් වේ. උදාහරණයක් ලෙස, මුදල් මාරු කිරීම (transfer) යනු එක් database write එකක් පමණක් නොවේ; එය ගිණුම් දෙකක් කියවීම, ශේෂය වලංගු කිරීම, එකකින් අඩු කිරීම, අනෙකට එකතු කිරීම, සහ ගිණුම් දෙකම සුරැකීම වැනි පියවර කිහිපයකින් සමන්විත ක්‍රියාවලියකි.

5.1 පාරිභෝගික කළමනාකරණය (Customer Management)

පළමු පියවර වන්නේ අපගේ ගනුදෙනුකරුවන් කළමනාකරණය කිරීමයි. මේ සඳහා `Customer` entity එකක් නිර්මාණය කර එය `Account` entity එක සමඟ ඇති සම්බන්ධතාවය නිර්වචනය කිරීම අවශ්‍ය වේ. එක් පාරිභෝගිකයෙකුට ගිණුම් කිහිපයක් තිබිය හැකිය. මෙය සම්භාව්‍ය One-to-Many සම්බන්ධතාවයකි.


// --- Customer.java Entity ---
@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    
    // එක් Customer කෙනෙකුට Accounts ගොඩක් තිබිය හැකිය (One-to-Many).
    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private List<Account> accounts = new ArrayList<>();
    
    // Getters and Setters...
}

// --- Account.java Entity එක තුළ ---
// මෙම සම්බන්ධතාවය Customer වෙත ආපසු එක් කරන්න
@ManyToOne // Accounts ගොඩකට ඇත්තේ එක් Customer කෙනෙකි (Many-to-One).
@JoinColumn(name = "customer_id") // 'accounts' table එකේ 'customer_id' foreign key column එකක් සාදයි.
private Customer customer;

5.2 ගිණුම් කළමනාකරණය (Account Management)

දැන් අපට ගිණුම් කළමනාකරණය සඳහා APIs ගොඩනැගිය හැකිය. සෑම නව ගිණුමක්ම පවතින පාරිභෝගිකයෙකුට නිසියාකාරව සම්බන්ධ කර ඇති බවට මෙයින් සහතික වෙමු.

ගිණුම් සඳහා API Endpoints


// --- AccountController.java ---
@RestController
@RequestMapping("/api/accounts")
public class AccountController {

    @Autowired
    private AccountService accountService;

    // නිශ්චිත පාරිභෝගිකයෙකු සඳහා නව ගිණුමක් විවෘත කිරීම
    @PostMapping("/customer/{customerId}")
    public Account openAccount(@PathVariable Long customerId, @RequestBody Account account) {
        return accountService.createAccountForCustomer(customerId, account);
    }
    
    // ගිණුම් ශේෂය බැලීම
    @GetMapping("/{accountId}/balance")
    public double getAccountBalance(@PathVariable Long accountId) {
        return accountService.getAccountBalance(accountId);
    }
}

5.3 ගනුදෙනු (Transactions): මූලික Business Logic

මෙය අපගේ බැංකු යෙදුමේ හදවතයි. අපි මුදල් තැන්පත් කිරීම, ආපසු ගැනීම සහ මාරු කිරීම සඳහා වන logic එක ක්‍රියාත්මක කරන්නෙමු. මෙම මෙහෙයුම් transactional විය යුතුය—එනම් ඒවා සම්පූර්ණයෙන්ම සාර්ථක විය යුතුය, නැතහොත් කිසිසේත්ම සිදු නොවිය යුතුය. මෙම දත්ත අඛණ්ඩතාව සහතික කිරීම සඳහා අපි අපගේ service methods මත @Transactional annotation එක භාවිතා කරමු.

මුදල් තැන්පත් කිරීමේ සහ ආපසු ගැනීමේ Logic


// --- AccountService.java තුළ ---

@Transactional
public Account deposit(Long accountId, double amount) {
    Account account = accountRepository.findById(accountId)
        .orElseThrow(() -> new RuntimeException("ගිණුම හමු නොවීය"));
    
    account.setBalance(account.getBalance() + amount);
    return accountRepository.save(account);
}

@Transactional
public Account withdraw(Long accountId, double amount) {
    Account account = accountRepository.findById(accountId)
        .orElseThrow(() -> new RuntimeException("ගිණුම හමු නොවීය"));
    
    if (account.getBalance() < amount) {
        throw new RuntimeException("ප්‍රමාණවත් මුදලක් නොමැත.");
    }

    account.setBalance(account.getBalance() - amount);
    return accountRepository.save(account);
}

මුදල් මාරු කිරීමේ (Fund Transfer) Logic

මෙය අපගේ වඩාත්ම සංකීර්ණ business logic කොටසයි. එයට ගිණුම් දෙකක් ඇතුළත් වන අතර එය තනි පරමාණුක මෙහෙයුමක් (single atomic operation) ලෙස හැසිරවිය යුතුය.


// --- AccountService.java තුළ ---

@Transactional
public void transfer(Long fromAccountId, Long toAccountId, double amount) {
    // 1. මූලාශ්‍ර ගිණුමෙන් හර කිරීම (අපගේ withdraw logic එක නැවත භාවිතා කරමින්)
    withdraw(fromAccountId, amount);
    
    // 2. ගමනාන්ත ගිණුමට බැර කිරීම (අපගේ deposit logic එක නැවත භාවිතා කරමින්)
    deposit(toAccountId, amount);
}
@Transactional මෙහිදී තීරණාත්මක වන්නේ ඇයි? සිතන්න, withdraw ක්‍රියාවලිය සාර්ථක වී, නමුත් deposit ක්‍රියාවලියට පෙර යෙදුම බිඳ වැටුණහොත් කුමක් සිදුවේද? මුදල් අතුරුදහන් වනු ඇත! @Transactional annotation එක මගින් සහතික කරන්නේ transfer method එකේ යම් කොටසක් අසාර්ථක වුවහොත්, ආරම්භක withdraw කිරීම "rollback" කර, දත්ත සමුදාය එහි මුල් තත්වයටම පත් කරන බවයි.

5.4 ගනුදෙනු ඉතිහාසය (Transaction History) API

බැංකුවක් සෑම ගනුදෙනුවක් පිළිබඳවම වාර්තාවක් තබා ගත යුතුය. මෙය සිදු කිරීම සඳහා, අපි නව `Transaction` entity එකක් නිර්මාණය කර, සෑම මූල්‍ය මෙහෙයුමක්ම වාර්තා කිරීම සඳහා අපගේ service methods වෙනස් කරන්නෙමු.

Transaction Entity එක


@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String type; // උදා: "DEPOSIT", "WITHDRAWAL", "TRANSFER"
    private double amount;
    private LocalDateTime timestamp;
    
    @ManyToOne
    @JoinColumn(name = "account_id")
    private Account account;
    
    // Constructors, Getters, සහ Setters...
}

History සඳහා API Endpoint එක

අවසාන වශයෙන්, නිශ්චිත ගිණුමක් සඳහා ඉතිහාසය ලබා ගැනීමට repository method එකක් සහ controller endpoint එකක් අපි නිර්මාණය කරමු.


// --- TransactionRepository.java ---
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
    // Spring Data JPA විසින් method නාමයෙන් query එක ස්වයංක්‍රීයව නිර්මාණය කරයි!
    List<Transaction> findByAccountIdOrderByTimestampDesc(Long accountId);
}

// --- AccountController.java තුළ ---
@GetMapping("/{accountId}/transactions")
public List<Transaction> getTransactionHistory(@PathVariable Long accountId) {
    return transactionService.getTransactionsForAccount(accountId);
}