Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ build/

### VS Code ###
.vscode/
.DS_Store
stockscope/src/main/java/com/.DS_Store
stockscope/.DS_Store
stockscope/.DS_Store
19 changes: 19 additions & 0 deletions ArchitecturalDesign/ArchitecturalCompliance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Architecture Compliance

## Overview
The system has been designed using a layered structure based on Clean Architecture principles. This approach was chosen to keep the application organised and to make it easier to maintain and extend as new features are added in later stages of development.

## Layered Structure
The architecture is divided into four main layers: presentation, application, domain, and infrastructure. The presentation layer is responsible for handling user interaction through the Web UI and passing requests to the controller. The application layer manages the main flow of the system, including retrieving share price data, processing it, and preparing it for comparison. The domain layer represents the core concepts of the system and remains independent from technical concerns. The infrastructure layer handles external operations such as accessing market data and storing cached data locally.

## Dependency Direction
The system follows the principle that dependencies should flow inward. This means that outer layers depend on inner layers, but inner layers do not depend on outer layers. As a result, the core logic of the system remains independent from frameworks, APIs, and storage mechanisms. This makes the design more stable, as changes in external components do not directly affect the core of the application.

## Use of Interfaces
Interfaces are used to separate the application logic from the infrastructure layer. The application layer defines the required behaviour through interfaces, while the infrastructure layer provides the concrete implementations. For example, data retrieval and caching are handled through interfaces, allowing different implementations to be used without changing the main logic of the system.

## Separation of Concerns
The design ensures that responsibilities are clearly separated. Business logic is contained within the service layer and is not mixed with API calls or database operations. This helps to avoid tightly coupled code and makes the system easier to understand and maintain.

## Summary
Overall, the system follows Clean Architecture principles by organising components into clear layers, controlling the direction of dependencies, and using interfaces to reduce coupling. This results in a design that is flexible, maintainable, and suitable for future development.
26 changes: 26 additions & 0 deletions ArchitecturalDesign/BusinessConceptModel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Business Concept Model

## Overview
This diagram shows the main ideas behind the share price comparison app and how they connect. It focuses on what the system does from a user point of view rather than how it is built.

## Business Concept Model Diagram
<img width="933" height="638" alt="image" src="https://github.com/user-attachments/assets/61790d27-730b-4367-9333-5922f7b284ae" />

## Main Concepts
The centre of the model is the Comparison. This represents the main task in the system where a user compares share prices over time.

A User can create multiple comparisons which shows that users can analyse different shares whenever they want.

Each comparison uses a Date Range. This defines the time period for the data being viewed.

The comparison is carried out on one or two Shares. Each share belongs to a Company which represents the business that owns the stock.

Each share has a Price History which stores all price data over time. This is made up of multiple Price Records where each record represents the price on a specific day.

The results are shown using a Chart which displays the comparison in a clear visual way.

## Relationships
The diagram shows how these concepts are linked. A user requests a comparison, a comparison uses a date range and compares shares, and each share is connected to its company and price data. The chart then displays the final result.

## Summary
This model gives a clear view of the key concepts in the system and how they relate to each other. It helps explain the problem before moving on to the technical design in later stages.
9 changes: 9 additions & 0 deletions ArchitecturalDesign/BusinessTypeModel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Business Type Model

<img width="751" height="453" alt="Business Type Model" src="https://github.com/user-attachments/assets/fe308c06-9209-4aea-810f-67133d445f88" />

### Notes
- Derived from previous models/diagrams
- Aligned with Java code
- Date ranges must not exceed two years
- Focussed only on domain types, core design only
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<img width="817" height="632" alt="image" src="https://github.com/user-attachments/assets/447eda06-dd11-4039-bd2a-eb2400fb0771" />

![Component Specification Diagram](component-diagram.png)


## Architectural Concepts and Their Application
Expand Down
19 changes: 19 additions & 0 deletions ArchitecturalDesign/DependencyStructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Review of Dependency Structure

## Overview
The dependency structure of the system was reviewed to ensure that components are organised correctly and that dependencies follow a clear and logical direction. The aim was to confirm that the system avoids tight coupling and supports a maintainable design.

## Dependency Relationships
The system does not contain any circular dependencies between classes or packages. Each component depends only on what is necessary, and the overall flow of control is clear. The user interacts with the UI, which passes requests to the controller. The controller then calls the service layer, and the service layer interacts with external functionality through defined interfaces. This creates a clear and predictable flow through the system.

## Layer Interaction
The infrastructure layer depends on interfaces defined in the application layer rather than the other way around. This ensures that the core logic of the system is not directly tied to specific implementations. As a result, changes to external systems such as APIs or storage mechanisms can be made without affecting the main application logic.

## Loose Coupling and Modularity
The structure supports loose coupling by separating responsibilities across layers and using interfaces between them. This means that individual components can be modified or replaced without causing issues in other parts of the system. The package structure reflects this separation, with distinct layers for presentation, application, domain, and infrastructure.

## Testability and Scalability
The dependency structure also improves testability, as components can be tested independently by using interfaces instead of concrete implementations. It also supports scalability, since new features or components can be added without disrupting the existing system. This makes the design more suitable for future development.

## Summary
Overall, the dependency structure is clear, logical, and well organised. It avoids unnecessary dependencies, maintains loose coupling, and supports both testing and future growth of the system.
41 changes: 41 additions & 0 deletions ArchitecturalDesign/UseCase.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Use Case Model

## Overview
This use case model describes how the user interacts with the Share Price Comparison Web App. It focuses on the main tasks the user can perform and how the system responds to those actions. The diagram identifies the key use cases and shows how some actions depend on others.

## Use Case Diagram
<img width="818" height="611" alt="image" src="https://github.com/user-attachments/assets/61e5d7bf-6b55-4a5e-9ea8-6ab9c8ea5014" />

## Main Use Cases
The user can retrieve share price data, compare share prices, and view share price charts. These represent the main goals of the user when using the application.

The system also carries out supporting actions such as storing price data and retrieving cached data. These actions are not directly triggered by the user but are required for the system to function properly and to support offline use.

## Use Case Description: Compare Share Prices

### Actor
User

### Description
This use case allows the user to compare the share prices of one or two companies over a selected date range. The system retrieves the relevant data and presents it in a chart so that the user can easily compare performance over time.

### Precondition
The user has access to the application and enters valid share symbols and a date range within the allowed limit.

### Main Flow
1. The user enters one or two share symbols.
2. The system validates the input.
3. The user selects a date range.
4. The system retrieves share price data for the selected shares from an external source.
5. The system stores the retrieved data locally for future use.
6. The system processes the data to prepare it for comparison.
7. The system generates a chart based on the processed data.
8. The system displays the chart to the user.

### Alternative Flow
If there is no internet connection, the system retrieves previously stored data instead of fetching new data.

If the user enters invalid share symbols or an invalid date range, the system displays an error message and requests valid input.

### Postcondition
The user is presented with a chart showing the comparison of share prices over the selected time period, and the data may be stored for later use.
Binary file added ArchitecturalDesign/component-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Develop architecture from requirements using formal models

### Member Responsibilities

**Member A**
**Member A - Azlan**
- Create Business Concept Model
- Create Use Case Diagram
- Write detailed use case descriptions
Expand Down
41 changes: 41 additions & 0 deletions Meeting-Documentation/Meeting-4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Meeting Report — 06/03/2026 (13:00–14:00)

## Project
**Stock Comparison Application**

## Meeting Goal
- Finalise system architecture
- Make sure it follows Clean Architecture
- Review business type model

## Roles
- **Facilitator:** Luke Pring
- **Note taker:** Sameer Shabbir

## Attendees
- Luke Pring
- Jack Turner
- Azlan Rashid
- Sameer Shabbir

## Roundtable Updates
- **Luke:** Advised to base the business type model on the business concept model
- **Jack:** Worked on UI and reviewed system structure
- **Azlan:** Finalised architecture diagram
- **Sameer:** Updated meeting notes and GitHub

## Discussion points
- Confirmed layered architecture (presentation, application, infrastructure)
- Agreed to use interfaces for better structure
- Reviewed system flow (UI → Controller → Service → Data)
- Confirmed use of API + cache for data

## Actions
- Upload architecture diagram (**Sameer**)
- Complete business type model (**Azlan**)
- Continue UI work (**Jack**)
- Review and confirm architecture design (**Luke**)
- Update GitHub (**All**)

## Outcome
Architecture completed and uploaded. System follows a clear layered structure and meets Sprint 2 requirements.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sharecomparison.application;

import com.sharecomparison.domain.ChartData;
import com.sharecomparison.domain.ComparisonResult;
import com.sharecomparison.domain.PriceData;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;

@Component
public class CompareSharesUseCase {

private final IChartBuilder chartBuilder;

public CompareSharesUseCase(IChartBuilder chartBuilder) {
this.chartBuilder = chartBuilder;
}

public ComparisonResult compare(String symbol1, String symbol2,
LocalDate startDate, LocalDate endDate,
List<PriceData> data1, List<PriceData> data2) {

ChartData chartData = chartBuilder.buildChart(data1, data2);

return new ComparisonResult(symbol1, symbol2, startDate, endDate, data1, data2, chartData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.sharecomparison.application;

import com.sharecomparison.domain.PriceData;
import com.sharecomparison.infrastructure.ICacheStore;
import com.sharecomparison.infrastructure.IMarketDataClient;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;

@Component
public class FetchSharePricesUseCase {

private final ICacheStore cacheStore;
private final IMarketDataClient marketDataClient;

public FetchSharePricesUseCase(ICacheStore cacheStore, IMarketDataClient marketDataClient) {
this.cacheStore = cacheStore;
this.marketDataClient = marketDataClient;
}

public List<PriceData> fetch(String symbol, LocalDate startDate, LocalDate endDate) {
String cacheKey = symbol + ":" + startDate + ":" + endDate;

List<PriceData> cached = cacheStore.load(cacheKey);
if (cached != null && !cached.isEmpty()) {
return cached;
}

List<PriceData> fetched = marketDataClient.fetch(symbol, startDate, endDate);

if (fetched != null && !fetched.isEmpty()) {
cacheStore.save(cacheKey, fetched);
}

return fetched;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.sharecomparison.application;

import com.sharecomparison.domain.ChartData;
import com.sharecomparison.domain.ComparisonResult;
import com.sharecomparison.domain.PriceData;
import com.sharecomparison.infrastructure.ICacheStore;
import com.sharecomparison.infrastructure.IMarketDataClient;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
Expand All @@ -13,29 +10,38 @@
@Service
public class PriceComparisonService implements IPriceComparisonService {

private final ICacheStore cacheStore;
private final IMarketDataClient marketDataClient;
private final IChartBuilder chartBuilder;
private final FetchSharePricesUseCase fetchSharePricesUseCase;
private final CompareSharesUseCase compareSharesUseCase;

public PriceComparisonService(ICacheStore cacheStore,
IMarketDataClient marketDataClient,
IChartBuilder chartBuilder) {
this.cacheStore = cacheStore;
this.marketDataClient = marketDataClient;
this.chartBuilder = chartBuilder;
public PriceComparisonService(FetchSharePricesUseCase fetchSharePricesUseCase,
CompareSharesUseCase compareSharesUseCase) {
this.fetchSharePricesUseCase = fetchSharePricesUseCase;
this.compareSharesUseCase = compareSharesUseCase;
}

@Override
public ComparisonResult compare(String symbol1, String symbol2,
LocalDate startDate, LocalDate endDate) {

List<PriceData> data1 = marketDataClient.fetch(symbol1, startDate, endDate);
List<PriceData> data2 = marketDataClient.fetch(symbol2, startDate, endDate);
validateDateRange(startDate, endDate);

cacheStore.save(symbol1, data1);
cacheStore.save(symbol2, data2);
List<PriceData> data1 = fetchSharePricesUseCase.fetch(symbol1, startDate, endDate);
List<PriceData> data2 = fetchSharePricesUseCase.fetch(symbol2, startDate, endDate);

ChartData chartData = chartBuilder.buildChart(data1, data2);
return new ComparisonResult(symbol1, symbol2, startDate, endDate, data1, data2, chartData);
return compareSharesUseCase.compare(symbol1, symbol2, startDate, endDate, data1, data2);
}
}

private void validateDateRange(LocalDate start, LocalDate end) {
if (start == null || end == null) {
throw new IllegalArgumentException("Start and End dates must be provided.");
}

if (start.plusYears(2).isBefore(end)) {
throw new IllegalArgumentException("The maximum date range permitted is two years.");
}

if (start.isAfter(end)) {
throw new IllegalArgumentException("Start date cannot be after end date.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.sharecomparison.infrastructure;

public interface ICacheStore {

void save(String key, Object data);
import com.sharecomparison.domain.PriceData;
import java.util.List;

Object load(String key);
public interface ICacheStore {
void save(String key, List<PriceData> data);
List<PriceData> load(String key);
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package com.sharecomparison.infrastructure;

import com.sharecomparison.domain.PriceData;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class LocalCacheStore implements ICacheStore {

private final Map<String, Object> cache = new HashMap<>();
private final Map<String, List<PriceData>> cache = new HashMap<>();

@Override
public void save(String key, Object data) {
public void save(String key, List<PriceData> data) {
cache.put(key, data);
}

@Override
public Object load(String key) {
public List<PriceData> load(String key) {
return cache.get(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,3 @@ public String handleDateParseError(MethodArgumentTypeMismatchException ex, Model
return "index";
}
}
}
Loading