JK
>ΦΟΡΤΩΣΗ // ΦΟΡΤΩΣΗ
ΑΡΧΙΚΟΠΟΙΗΣΗ ΠΡΟΒΟΛΗΣ
Λογιστικό καθολικό διπλογραφίας και backend διαχείρισης μετοχών με αποτίμηση σε INR και πλήρη έλεγχο.
Η διαχείριση μετοχικών δικαιωμάτων απαιτεί απόλυτη συναλλακτική ακρίβεια και ανθεκτικότητα σε διακοπές εξωτερικών πηγών τιμών.
| Τεχνολογία | Έκδοση | Σκοπός | Γιατί επιλέχθηκε |
|---|---|---|---|
| Go | 1.25.1 | Γλώσσα προγραμματισμού | Ισχυρή stdlib, πρωτόγονα ταυτοχρονισμού, γρήγορη μεταγλώττιση |
| Gin | 1.11.0 | Πλαίσιο HTTP, δρομολόγηση, middleware | Υψηλή απόδοση, υποστήριξη middleware, ενεργή κοινότητα |
| PostgreSQL | 17 | Σχεσιακή βάση δεδομένων | Προηγμένες δυνατότητες SQL (όψεις, LATERAL, παραγόμενες στήλες, ENUM) |
| golang-migrate | v4.19.0 | Μεταναστεύσεις βάσης δεδομένων | Απλός εκτελεστής μεταναστεύσεων βασισμένος σε αρχεία |
| logrus | 1.9.3 | Δομημένη καταγραφή | Δομημένα πεδία, καταγραφή με επίπεδα |
| go-sqlmock | v1.5.2 | Mocking βάσης για tests | Προσομοιώνει SQL για unit tests χωρίς πραγματική βάση |
| lib/pq | v1.10.9 | Driver PostgreSQL | Δοκιμασμένο, εγγενής υποστήριξη PostgreSQL |
| Docker | — | Containerization | Αναπαραγώγιμες κατασκευές, απομονωμένα περιβάλλοντα |
| Μέθοδος | Διαδρομή | Σκοπός |
|---|---|---|
| GET | /health | Έλεγχος υγείας — επιστρέφει {status: OK} |
| POST | /api/v1/reward | Δημιουργία ανταμοιβής — idempotent, συναλλακτική, υπολογισμός προμηθειών |
| POST | /api/v1/adjustments/:id | Εφαρμογή προσαρμογής — κλείδωμα γραμμής, λογιστικές εγγραφές ανά τύπο |
| GET | /api/v1/today-stocks/:userId | Σημερινές ανταμοιβές από την όψη today_rewards |
| GET | /api/v1/historical-inr/:userId | Ιστορική αποτίμηση INR — με σελιδοποίηση, πολλαπλασιασμένη με γεγονότα |
| GET | /api/v1/stats/:userId | Σύνοψη στατιστικών — σημερινές ανταμοιβές + συνολική αξία χαρτοφυλακίου |
| GET | /api/v1/portfolio/:userId | Λεπτομέρειες χαρτοφυλακίου — με σελιδοποίηση, φιλτραρισμένες για διαγραφή από την αγορά |
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| username | VARCHAR(50) | NOT NULL, UNIQUE |
| VARCHAR(100) | NOT NULL, UNIQUE | |
| created_at | TIMESTAMP | Προεπιλογή: NOW() |
Λογαριασμοί χρηστών. Αρχικοποιημένοι με έναν δοκιμαστικό χρήστη.
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| user_id | INT | FK → users(id) ON DELETE CASCADE |
| stock_symbol | VARCHAR(20) | NOT NULL |
| quantity | NUMERIC(18,6) | NOT NULL, αρνητικό = ακύρωση |
| idempotency_key | VARCHAR(64) | UNIQUE, επιτρέπει NULL |
| reward_date | DATE | GENERATED ALWAYS AS (created_at::DATE) STORED |
Βασικά γεγονότα ανταμοιβών. Μοναδικότητα σε (user_id, stock_symbol, reward_date) με παραλλαγή χωρίς διάκριση πεζών-κεφαλαίων.
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| reward_id | INT | FK → rewards(id) ON DELETE CASCADE |
| entry_type | ledger_entry_type | ENUM: stock_units, inr_outflow, fees, brokerage_fee, stt_fee, gst_fee |
| stock_symbol | VARCHAR(20) | Επιτρέπει NULL |
| quantity | NUMERIC(18,6) | Επιτρέπει NULL |
| amount | NUMERIC(18,4) | Επιτρέπει NULL |
Λογιστικό βιβλίο διπλής εγγραφής. Κάθε ανταμοιβή δημιουργεί 2-5 εγγραφές.
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| stock_symbol | VARCHAR(20) | UNIQUE |
| price | NUMERIC(18,4) | Τρέχουσα τιμή |
| updated_at | TIMESTAMP | Ενημερώνεται από το PriceService |
Τελευταία γνωστή τιμή ανά μετοχή. Ενημερώνεται από το PriceService υποβάθρου.
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| stock_symbol | VARCHAR(20) | NOT NULL |
| price | NUMERIC(18,4) | NOT NULL |
| date | DATE | NOT NULL, UNIQUE με stock_symbol |
Καθημερινά στιγμιότυπα τιμών για ιστορική αποτίμηση. Upsert από το PriceService.
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| stock_symbol | VARCHAR(20) | NOT NULL |
| event_type | stock_event_type | ENUM: split, bonus, merge, delist |
| ratio_num | NUMERIC(18,6) | Αριθμητής πολλαπλασιαστή |
| ratio_den | NUMERIC(18,6) | Παρονομαστής πολλαπλασιαστή |
| new_symbol | VARCHAR(20) | Για συγχωνεύσεις |
| effective_date | TIMESTAMP | NOT NULL |
Εταιρικές ενέργειες. Το ratio_num/ratio_den ορίζει τον πολλαπλασιαστή (π.χ. split 2:1).
| Στήλη | Τύπος | Σημειώσεις |
|---|---|---|
| idPK | SERIAL | Δημιουργείται αυτόματα |
| reward_id | INT | FK → rewards(id) ON DELETE CASCADE |
| adjustment_type | VARCHAR(64) | reward_reversal, fee_refund, manual_correction |
| delta_quantity | NUMERIC(18,6) | NOT NULL |
| delta_amount | NUMERIC(18,4) | NOT NULL |
| reason | VARCHAR(255) | Επιτρέπει NULL |
Τροποποιήσεις υφιστάμενων ανταμοιβών με κλείδωμα γραμμής.
Ένα-προς-πολλά: users→rewards (CASCADE), rewards→ledger (CASCADE), rewards→adjustments (CASCADE). Έμμεσες διασταυρούμενες αναφορές: rewards.stock_symbol ↔ stock_prices.stock_symbol (μέσω UPPER()), rewards.stock_symbol ↔ stock_events.stock_symbol (πολλαπλασιαστές γεγονότων). Δεν υπάρχει τυπικό FK μεταξύ rewards και stock_prices/stock_events· επιβάλλεται σε επίπεδο όψης.
Απόφαση: Οι σύνθετοι υπολογισμοί χαρτοφυλακίου/ιστορικού υλοποιούνται ως όψεις PostgreSQL αντί για κώδικα εφαρμογής
Λόγος: Μειώνει την πολυπλοκότητα της εφαρμογής· αξιοποιεί τον βελτιστοποιητή ερωτημάτων PostgreSQL· μία πηγή αλήθειας για τους υπολογισμούς
Οφέλη: Συνεπής υπολογισμός σε όλα τα endpoints· η βάση χειρίζεται αποδοτικά JOIN και συγκεντρώσεις
Ανταλλάγματα: Οι όψεις μπορεί να είναι αργές σε μεγάλα σύνολα δεδομένων· δυσκολότερος εντοπισμός σφαλμάτων από τον κώδικα εφαρμογής· εξάρτηση από PostgreSQL
Απόφαση: Χρήση EXP(SUM(LN(ratio))) για τον υπολογισμό αθροιστικών πολλαπλασιαστών εταιρικών γεγονότων
Λόγος: Αποφεύγει υπερχείλιση/υποχείλιση κινητής υποδιαστολής όταν συνδυάζονται πολλά γεγονότα
Οφέλη: Αριθμητικά σταθερό· χειρίζεται οποιονδήποτε αριθμό γεγονότων
Ανταλλάγματα: Λιγότερο ευανάγνωστο από τον άμεσο πολλαπλασιασμό· απαιτεί θετικές αναλογίες
Απόφαση: Η επιχειρηματική λογική βρίσκεται απευθείας στους χειριστές χωρίς ξεχωριστό επίπεδο υπηρεσίας
Λόγος: Απλούστερο για μικρό έργο· λιγότερα αρχεία και αφαιρέσεις προς συντήρηση
Οφέλη: Λίγος boilerplate κώδικας· εύκολη παρακολούθηση της ροής αιτήματος
Ανταλλάγματα: Δύσκολη επαναχρησιμοποίηση λογικής· ισχυρή σύζευξη HTTP και πρόσβασης δεδομένων· δύσκολος ανεξάρτητος έλεγχος επιχειρηματικής λογικής
Απόφαση: Χρήση RandomPriceFetcher αντί για πραγματικό API δεδομένων αγοράς
Λόγος: Περιβάλλον ανάπτυξης/δοκιμών· δεν απαιτείται διαχείριση κλειδιών API
Οφέλη: Μηδενικές εξωτερικές εξαρτήσεις· σχεδόν ντετερμινιστική συμπεριφορά
Ανταλλάγματα: Όχι έτοιμο για παραγωγή· οι αξίες χαρτοφυλακίου είναι χωρίς νόημα· πρέπει να αντικατασταθεί για πραγματική ανάπτυξη
Απόφαση: Χρήση SAVEPOINT της PostgreSQL για διαχείριση idempotency χωρίς ματαίωση της εξωτερικής συναλλαγής
Λόγος: Επιτρέπει ασφάλεια επαναλήψεων χωρίς εξωτερική υπηρεσία idempotency
Οφέλη: Εξελιγμένο idempotency εντός συναλλαγής που λειτουργεί εντός των περιορισμών της PostgreSQL
Ανταλλάγματα: Σύνθετη διαχείριση σφαλμάτων· επιβάρυνση SAVEPOINT
Απόφαση: Όλα τα endpoints είναι δημόσια προσβάσιμα χωρίς auth
Λόγος: Έργο ανάπτυξης/επίδειξης· το auth αναμένεται να προστεθεί αργότερα (π.χ. μέσω API gateway)
Οφέλη: Απλούστερη υλοποίηση· ευκολότερη δοκιμή
Ανταλλάγματα: Ακατάλληλο για παραγωγή· οποιοσδήποτε πελάτης μπορεί να δημιουργήσει ανταμοιβές ή να εφαρμόσει προσαρμογές