Skip to content

Commit cfbc091

Browse files
authored
Merge pull request #25 from NLipatov/feature/2
Feature/2
2 parents e2af710 + efd713e commit cfbc091

File tree

17 files changed

+871
-52
lines changed

17 files changed

+871
-52
lines changed

src/application/lavatop_use_cases.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,47 @@ package application
22

33
import (
44
"fmt"
5+
"goproxy/dal/cache_serialization"
56
"goproxy/domain/lavatopsubdomain/lavatopaggregates"
67
"goproxy/domain/lavatopsubdomain/lavatopvalueobjects"
8+
"time"
9+
)
10+
11+
const (
12+
lavaTopUseCasesCacheTtl = time.Hour * 12
713
)
814

915
type LavaTopUseCases struct {
10-
invoiceRepository InvoiceRepository[lavatopaggregates.Invoice]
11-
billingService BillingService[lavatopaggregates.Invoice, lavatopvalueobjects.Offer]
16+
invoiceRepository InvoiceRepository[lavatopaggregates.Invoice]
17+
billingService BillingService[lavatopaggregates.Invoice, lavatopvalueobjects.Offer]
18+
cache CacheWithTTL[[]cache_serialization.LavaTopOfferDto]
19+
offerCacheSerializer cache_serialization.CacheSerializer[lavatopvalueobjects.Offer, cache_serialization.LavaTopOfferDto]
1220
}
1321

14-
func NewLavaTopUseCases(billingService BillingService[lavatopaggregates.Invoice, lavatopvalueobjects.Offer]) LavaTopUseCases {
22+
func NewLavaTopUseCases(billingService BillingService[lavatopaggregates.Invoice, lavatopvalueobjects.Offer],
23+
cache CacheWithTTL[[]cache_serialization.LavaTopOfferDto]) LavaTopUseCases {
1524
return LavaTopUseCases{
16-
billingService: billingService,
25+
billingService: billingService,
26+
cache: cache,
27+
offerCacheSerializer: cache_serialization.NewLavaTopOfferCacheSerializer(),
1728
}
1829
}
1930

2031
func (l *LavaTopUseCases) GetOffers() ([]lavatopvalueobjects.Offer, error) {
21-
return l.billingService.GetOffers()
32+
cached, cachedErr := l.cache.Get("lavatop_use_cases:get_offers")
33+
if cachedErr == nil {
34+
return l.offerCacheSerializer.ToTArray(cached), nil
35+
}
36+
37+
offers, offersErr := l.billingService.GetOffers()
38+
if offersErr != nil {
39+
return nil, offersErr
40+
}
41+
42+
_ = l.cache.Set("lavatop_use_cases:get_offers", l.offerCacheSerializer.ToDArray(offers))
43+
_ = l.cache.Expire("lavatop_use_cases:get_offers", lavaTopUseCasesCacheTtl)
44+
45+
return offers, nil
2246
}
2347

2448
func (l *LavaTopUseCases) PublishInvoice(invoice lavatopaggregates.Invoice) (lavatopaggregates.Invoice, error) {

src/application/repository.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ type EventRepository interface {
2828

2929
type PlanRepository interface {
3030
Repository[aggregates.Plan]
31-
GetAll() (aggregates.Plan, error)
31+
GetAll() ([]aggregates.Plan, error)
32+
GetAllWithFeatures() ([]aggregates.Plan, error)
3233
GetByName(name string) (aggregates.Plan, error)
3334
GetByNameWithFeatures(name string) (aggregates.Plan, error)
3435
GetByIdWithFeatures(id int) (aggregates.Plan, error)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package cache_serialization
2+
3+
import (
4+
"goproxy/domain/aggregates"
5+
"goproxy/domain/valueobjects"
6+
"time"
7+
)
8+
9+
type PlanFeatureDto struct {
10+
PlanId int `json:"plan_id"`
11+
FeatureName string `json:"name"`
12+
FeatureDescription string `json:"description"`
13+
}
14+
15+
type PlanDto struct {
16+
Id int `json:"id"`
17+
Name string `json:"name"`
18+
BytesLimit int64 `json:"bytes_limit"`
19+
Duration int `json:"duration"`
20+
Features []PlanFeatureDto `json:"features"`
21+
CreatedAt time.Time `json:"created_at"`
22+
}
23+
24+
type AggegatePlanCacheSerializer struct{}
25+
26+
func NewAggegatePlanCacheSerializer() CacheSerializer[aggregates.Plan, PlanDto] {
27+
return &AggegatePlanCacheSerializer{}
28+
}
29+
30+
func (a *AggegatePlanCacheSerializer) ToT(dto PlanDto) aggregates.Plan {
31+
features := make([]valueobjects.PlanFeature, len(dto.Features))
32+
for i, v := range dto.Features {
33+
features[i] = valueobjects.NewPlanFeature(v.PlanId, v.FeatureName, v.FeatureDescription)
34+
}
35+
36+
plan, _ := aggregates.NewPlan(dto.Id, dto.Name, dto.BytesLimit, dto.Duration, features)
37+
return plan
38+
}
39+
40+
func (a *AggegatePlanCacheSerializer) ToTArray(dtos []PlanDto) []aggregates.Plan {
41+
arr := make([]aggregates.Plan, len(dtos))
42+
for i, dto := range dtos {
43+
arr[i] = a.ToT(dto)
44+
}
45+
46+
return arr
47+
}
48+
49+
func (a *AggegatePlanCacheSerializer) ToD(plan aggregates.Plan) PlanDto {
50+
features := make([]PlanFeatureDto, len(plan.Features()))
51+
for i, v := range plan.Features() {
52+
features[i] = PlanFeatureDto{
53+
PlanId: v.PlanId(),
54+
FeatureName: v.Feature(),
55+
FeatureDescription: v.Description(),
56+
}
57+
}
58+
return PlanDto{
59+
Id: plan.Id(),
60+
Name: plan.Name(),
61+
BytesLimit: plan.LimitBytes(),
62+
Duration: plan.DurationDays(),
63+
Features: features,
64+
CreatedAt: plan.CreatedAt(),
65+
}
66+
}
67+
68+
func (a *AggegatePlanCacheSerializer) ToDArray(plans []aggregates.Plan) []PlanDto {
69+
arr := make([]PlanDto, len(plans))
70+
for i, plan := range plans {
71+
arr[i] = a.ToD(plan)
72+
}
73+
74+
return arr
75+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cache_serialization
2+
3+
// CacheSerializer used for cache_serialization serialization and deserialization
4+
// T - target type, D - dto type
5+
type CacheSerializer[T any, D any] interface {
6+
ToT(dto D) T
7+
ToD(plan T) D
8+
ToTArray(dto []D) []T
9+
ToDArray(plans []T) []D
10+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package cache_serialization
2+
3+
import (
4+
"goproxy/domain/lavatopsubdomain/lavatopvalueobjects"
5+
)
6+
7+
type LavaTopOfferDto struct {
8+
ExtId string
9+
Name string
10+
Prices []LavaTopOfferPriceDto
11+
}
12+
13+
type LavaTopOfferPriceDto struct {
14+
Cents int64
15+
Periodicity lavatopvalueobjects.Periodicity
16+
Currency lavatopvalueobjects.Currency
17+
}
18+
19+
type LavaTopOfferCacheSerializer struct {
20+
}
21+
22+
func NewLavaTopOfferCacheSerializer() CacheSerializer[lavatopvalueobjects.Offer, LavaTopOfferDto] {
23+
return &LavaTopOfferCacheSerializer{}
24+
}
25+
26+
func (l *LavaTopOfferCacheSerializer) ToT(dto LavaTopOfferDto) lavatopvalueobjects.Offer {
27+
prices := make([]lavatopvalueobjects.Price, len(dto.Prices))
28+
for i, p := range dto.Prices {
29+
prices[i] = lavatopvalueobjects.NewPrice(p.Cents, p.Currency, p.Periodicity)
30+
}
31+
32+
return lavatopvalueobjects.NewOffer(dto.ExtId, dto.Name, prices)
33+
}
34+
func (l *LavaTopOfferCacheSerializer) ToD(offer lavatopvalueobjects.Offer) LavaTopOfferDto {
35+
prices := make([]LavaTopOfferPriceDto, len(offer.Prices()))
36+
37+
for i, p := range offer.Prices() {
38+
prices[i] = LavaTopOfferPriceDto{
39+
Cents: p.Cents(),
40+
Periodicity: p.Periodicity(),
41+
Currency: p.Currency(),
42+
}
43+
}
44+
45+
return LavaTopOfferDto{
46+
ExtId: offer.ExtId(),
47+
Name: offer.Name(),
48+
Prices: prices,
49+
}
50+
}
51+
func (l *LavaTopOfferCacheSerializer) ToTArray(dto []LavaTopOfferDto) []lavatopvalueobjects.Offer {
52+
result := make([]lavatopvalueobjects.Offer, len(dto))
53+
for i, d := range dto {
54+
result[i] = l.ToT(d)
55+
}
56+
return result
57+
}
58+
func (l *LavaTopOfferCacheSerializer) ToDArray(offer []lavatopvalueobjects.Offer) []LavaTopOfferDto {
59+
result := make([]LavaTopOfferDto, len(offer))
60+
for i, d := range offer {
61+
result[i] = l.ToD(d)
62+
}
63+
return result
64+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cache_serialization
2+
3+
import (
4+
"goproxy/domain/dataobjects"
5+
)
6+
7+
type PlanLavatopOfferDto struct {
8+
Id int
9+
PlanId int
10+
OfferId string
11+
}
12+
13+
type DataObjectsPlanLavatopOfferCacheSerializer struct {
14+
}
15+
16+
func NewPlanLavatopOfferSerializer() CacheSerializer[dataobjects.PlanLavatopOffer, PlanLavatopOfferDto] {
17+
return &DataObjectsPlanLavatopOfferCacheSerializer{}
18+
}
19+
20+
func (p *DataObjectsPlanLavatopOfferCacheSerializer) ToT(dto PlanLavatopOfferDto) dataobjects.PlanLavatopOffer {
21+
return dataobjects.NewPlanLavatopOffer(dto.Id, dto.PlanId, dto.OfferId)
22+
}
23+
24+
func (p *DataObjectsPlanLavatopOfferCacheSerializer) ToD(offer dataobjects.PlanLavatopOffer) PlanLavatopOfferDto {
25+
return PlanLavatopOfferDto{
26+
Id: offer.Id(),
27+
PlanId: offer.PlanId(),
28+
OfferId: offer.OfferId(),
29+
}
30+
}
31+
32+
func (p *DataObjectsPlanLavatopOfferCacheSerializer) ToTArray(dto []PlanLavatopOfferDto) []dataobjects.PlanLavatopOffer {
33+
result := make([]dataobjects.PlanLavatopOffer, len(dto))
34+
for i, d := range dto {
35+
result[i] = p.ToT(d)
36+
}
37+
return result
38+
}
39+
40+
func (p *DataObjectsPlanLavatopOfferCacheSerializer) ToDArray(plans []dataobjects.PlanLavatopOffer) []PlanLavatopOfferDto {
41+
result := make([]PlanLavatopOfferDto, len(plans))
42+
for i, plan := range plans {
43+
result[i] = PlanLavatopOfferDto{
44+
Id: plan.Id(),
45+
PlanId: plan.PlanId(),
46+
OfferId: plan.OfferId(),
47+
}
48+
}
49+
return result
50+
}

src/dal/repositories/mocks/cache_with_ttl.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package mocks
22

33
import (
44
"errors"
5+
"goproxy/application"
56
"sync"
67
"time"
78
)
@@ -16,7 +17,7 @@ type MockCacheWithTTL[T any] struct {
1617
mutex sync.RWMutex
1718
}
1819

19-
func NewMockCacheWithTTL[T any]() *MockCacheWithTTL[T] {
20+
func NewMockCacheWithTTL[T any]() application.CacheWithTTL[T] {
2021
return &MockCacheWithTTL[T]{
2122
data: make(map[string]mockCacheItem[T]),
2223
}

src/dal/repositories/plan_lavatop_offer_repository.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"goproxy/application"
8+
"goproxy/dal/cache_serialization"
89
"goproxy/domain/dataobjects"
910
"time"
1011
)
@@ -35,23 +36,25 @@ WHERE id = $1
3536
)
3637

3738
type PlanLavatopOfferRepository struct {
38-
db *sql.DB
39-
cache application.CacheWithTTL[[]dataobjects.PlanLavatopOffer]
39+
db *sql.DB
40+
cache application.CacheWithTTL[[]cache_serialization.PlanLavatopOfferDto]
41+
cachePlanLavatopOfferSerializer cache_serialization.CacheSerializer[dataobjects.PlanLavatopOffer, cache_serialization.PlanLavatopOfferDto]
4042
}
4143

4244
func NewPlanLavatopOfferRepository(db *sql.DB,
43-
cache application.CacheWithTTL[[]dataobjects.PlanLavatopOffer]) application.PlanOfferRepository {
45+
cache application.CacheWithTTL[[]cache_serialization.PlanLavatopOfferDto]) application.PlanOfferRepository {
4446
return &PlanLavatopOfferRepository{
45-
db: db,
46-
cache: cache,
47+
db: db,
48+
cache: cache,
49+
cachePlanLavatopOfferSerializer: cache_serialization.NewPlanLavatopOfferSerializer(),
4750
}
4851
}
4952

5053
func (p *PlanLavatopOfferRepository) GetOffers(planId int) ([]dataobjects.PlanLavatopOffer, error) {
5154
planKey := p.planToCacheKey(planId)
5255
cached, cachedErr := p.cache.Get(planKey)
5356
if cachedErr == nil {
54-
return cached, nil
57+
return p.cachePlanLavatopOfferSerializer.ToTArray(cached), nil
5558
}
5659

5760
var offers []dataobjects.PlanLavatopOffer
@@ -81,7 +84,7 @@ func (p *PlanLavatopOfferRepository) GetOffers(planId int) ([]dataobjects.PlanLa
8184
return make([]dataobjects.PlanLavatopOffer, 0), rows.Err()
8285
}
8386

84-
_ = p.cache.Set(planKey, offers)
87+
_ = p.cache.Set(planKey, p.cachePlanLavatopOfferSerializer.ToDArray(offers))
8588
_ = p.cache.Expire(planKey, cacheTtl)
8689

8790
if offers == nil {

src/dal/repositories/plan_lavatop_offer_repository_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"github.com/google/uuid"
77
"goproxy/application"
8+
"goproxy/dal/cache_serialization"
89
"goproxy/dal/repositories/mocks"
910
"goproxy/domain/dataobjects"
1011
"os"
@@ -29,9 +30,10 @@ func TestPlanLavatopOfferRepository(t *testing.T) {
2930
_ = db.Close()
3031
}(db)
3132

32-
cache := mocks.NewMockCacheWithTTL[[]dataobjects.PlanLavatopOffer]()
33-
planOfferRepo := NewPlanLavatopOfferRepository(db, cache)
34-
planRepo := NewPlansRepository(db)
33+
planOfferRepoCache := mocks.NewMockCacheWithTTL[[]cache_serialization.PlanLavatopOfferDto]()
34+
planRepoCache := mocks.NewMockCacheWithTTL[[]cache_serialization.PlanDto]()
35+
planOfferRepo := NewPlanLavatopOfferRepository(db, planOfferRepoCache)
36+
planRepo := NewPlansRepository(db, planRepoCache)
3537

3638
t.Run("GetOffers", func(t *testing.T) {
3739
ploCount := 5

0 commit comments

Comments
 (0)