Skip to content

Implemented design patterns consistently#443

Merged
Xhristin3 merged 3 commits into
rinafcode:mainfrom
Juwonlo:impid
Apr 27, 2026
Merged

Implemented design patterns consistently#443
Xhristin3 merged 3 commits into
rinafcode:mainfrom
Juwonlo:impid

Conversation

@Juwonlo
Copy link
Copy Markdown
Contributor

@Juwonlo Juwonlo commented Apr 26, 2026

Closes #371


Fixed Issue #371

Copilot AI review requested due to automatic review settings April 26, 2026 02:18
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 26, 2026

@Juwonlo Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR addresses Issue #371 by refactoring conditional logic in the explainability layer and introducing standardized logging documentation plus a NestJS logger implementation.

Changes:

  • Refactored dominant-signal explanation construction into a dedicated helper method and replaced some if/else chains with switch statements.
  • Added a NestJS LoggerService + LoggerModule with configurable log levels and structured JSON output in production.
  • Added documentation for logging strategy and design pattern usage across the ecosystem.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
recommendation-system/src/explainability/explainability.ts Extracts dominant-signal explanation logic into a helper and converts some branches to switch.
docs/logger.service.ts Introduces a configurable NestJS logger with JSON formatting in prod and colorized output in dev.
docs/logger.module.ts Exposes the logger as a global NestJS module.
docs/LOGGING_STRATEGY.md Defines required log levels, JSON schema, and best practices.
docs/DESIGN_PATTERNS.md Documents intended design patterns used across the codebase.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/logger.service.ts
Comment on lines +19 to +24
private shouldLog(level: string): boolean {
const levels = ['debug', 'info', 'warn', 'error'];
const configuredIndex = levels.indexOf(this.logLevel.toLowerCase());
const targetIndex = levels.indexOf(level);
return targetIndex >= configuredIndex;
}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this.logLevel is misconfigured (not in levels), configuredIndex becomes -1, causing shouldLog to return true for all valid level values (unexpectedly enabling all logs). Add a fallback when configuredIndex === -1 (e.g., default to info), or validate/normalize the configured level at construction time.

Copilot uses AI. Check for mistakes.
Comment thread docs/logger.service.ts
Comment on lines +47 to +53
const logEntry = {
timestamp,
level,
context: currentContext,
message: typeof message === 'object' ? JSON.stringify(message) : message,
...(Object.keys(metadata).length > 0 ? { data: metadata } : {}),
};
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.stringify(message) can throw on circular structures (and can also drop important info from Error objects). This can cause the logger itself to throw and potentially mask the original error. Consider a safe serialization approach (try/catch with a fallback string, or a safe-stringify/inspector-based formatter), and special-case Error to include name/message/stack.

Copilot uses AI. Check for mistakes.
Comment thread docs/LOGGING_STRATEGY.md
Comment on lines +16 to +20
*Note: The environment variable `LOG_LEVEL` dictates the minimum severity level output by the application.*

## 2. Structured Logging Format

All off-chain services MUST output logs in **Structured JSON format** when running in production (`NODE_ENV=production`). This ensures seamless ingestion by modern log aggregators and monitoring tools (e.g., Datadog, ELK, CloudWatch).
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conflicts with the added LoggerService, which reads app.logLevel (and app.nodeEnv) from ConfigService rather than LOG_LEVEL / NODE_ENV directly. Align the documentation with the actual configuration keys used by the logger, or update the logger to consume the documented env vars (potentially via Nest config mapping).

Suggested change
*Note: The environment variable `LOG_LEVEL` dictates the minimum severity level output by the application.*
## 2. Structured Logging Format
All off-chain services MUST output logs in **Structured JSON format** when running in production (`NODE_ENV=production`). This ensures seamless ingestion by modern log aggregators and monitoring tools (e.g., Datadog, ELK, CloudWatch).
*Note: The logger uses the application configuration key `app.logLevel` to determine the minimum severity level output by the application. If environment variables are used in a deployment, they should be mapped into `app.logLevel` via the Nest configuration layer rather than assumed to be read directly by the logger.*
## 2. Structured Logging Format
All off-chain services MUST output logs in **Structured JSON format** when running in production (`app.nodeEnv = production`). This ensures seamless ingestion by modern log aggregators and monitoring tools (e.g., Datadog, ELK, CloudWatch).

Copilot uses AI. Check for mistakes.
Comment thread docs/DESIGN_PATTERNS.md
Comment on lines +7 to +8
**Context**: Conditional branches that change behavior based on a type or flag (e.g., generating explanations for AI recommendations) often result in large, unmaintainable `switch` blocks.
**Usage**: Used in `ExplanationGenerator` (`explainability.ts`) to dynamically select the correct algorithm for extracting dominant signal explanations.
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current ExplanationGenerator implementation still uses a switch inside extractDominantSignalExplanation, so this doc describes a Strategy Pattern that isn't implemented by the code shown in this PR. Either (a) update this document to reflect the current refactor (helper method + switch), or (b) implement the documented strategy approach (e.g., a list/map of strategy objects selected by matches(...)).

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +80
private extractDominantSignalExplanation(
dominantSignal: string,
rankingSignal: any,
userProfile: Types.UserProfile,
similarUsers?: string[]
): {
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper introduces/propagates any for rankingSignal and a broad string for dominantSignal, which weakens type safety inside the method (e.g., property access on rankingSignal). Prefer using the same concrete type as the caller (e.g., a Types.RankingSignal-like interface) and narrow dominantSignal to a union/keyof type so invalid signal names are caught at compile time.

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +143
if (!userProfile) return ''; // Guard clause

Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userProfile is typed as a required parameter (userProfile: Types.UserProfile), so this guard is inconsistent with the method signature and can hide upstream contract violations by silently returning an empty explanation. Either remove the guard (preferred if the type contract is correct) or make the parameter optional/nullable in the signature and document the behavior.

Suggested change
if (!userProfile) return ''; // Guard clause

Copilot uses AI. Check for mistakes.
@Xhristin3 Xhristin3 merged commit 3fa7305 into rinafcode:main Apr 27, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement design patterns consistently

3 participants