Shopping cart management with reactive state and local caching
The Cart Component manages shopping cart operations including adding items, updating quantities, and observing cart state. It uses local caching for persistence and reactive flows for real-time updates.
Represents a user’s shopping cart with business logic for calculations.
package com.denisbrandi.androidrealca.cart.domain.modelimport com.denisbrandi.androidrealca.money.domain.model.Moneydata class Cart(val cartItems: List<CartItem>) { fun getSubtotal(): Money? { return if (cartItems.isNotEmpty()) { val currency = cartItems[0].money.currencySymbol var subtotal = 0.0 cartItems.forEach { subtotal += it.money.amount * it.quantity } Money(subtotal, currency) } else { null } } fun getNumberOfItems(): Int { return cartItems.sumOf { it.quantity } }}
Cart Business Logic
getSubtotal(): Calculates the total price of all items in the cart by multiplying each item’s price by its quantity. Returns null if cart is empty.getNumberOfItems(): Returns the total count of items, summing up all quantities (e.g., 2 apples + 3 oranges = 5 items).
package com.denisbrandi.androidrealca.cart.domain.modelimport com.denisbrandi.androidrealca.money.domain.model.Moneydata class CartItem( val id: String, val name: String, val money: Money, val imageUrl: String, val quantity: Int)
Implements the CartRepository interface with caching support.
package com.denisbrandi.androidrealca.cart.data.repositoryimport com.denisbrandi.androidrealca.cache.*import com.denisbrandi.androidrealca.cart.data.model.*import com.denisbrandi.androidrealca.cart.domain.model.*import com.denisbrandi.androidrealca.cart.domain.repository.CartRepositoryimport com.denisbrandi.androidrealca.money.domain.model.Moneyimport kotlinx.coroutines.flow.*internal class RealCartRepository( private val cacheProvider: CacheProvider) : CartRepository { private val flowCachedObject: FlowCachedObject<JsonCartCacheDto> by lazy { cacheProvider.getFlowCachedObject( fileName = "cart-cache", serializer = JsonCartCacheDto.serializer(), defaultValue = JsonCartCacheDto(emptyMap()) ) } override fun updateCartItem(userId: String, cartItem: CartItem) { val updatedCache = getUpdatedCacheForUser(userId) { userCart -> val cartItemInCache = userCart.find { it.id == cartItem.id } val cartItemDto = mapToDto(cartItem) if (cartItemInCache != null) { if (cartItem.quantity == 0) { userCart.remove(cartItemInCache) } else { val index = userCart.indexOf(cartItemInCache) userCart[index] = cartItemDto } } else { userCart.add(cartItemDto) } } flowCachedObject.put(updatedCache) } override fun observeCart(userId: String): Flow<Cart> { return flowCachedObject.observe().map { cachedDto -> mapToCart(userId, cachedDto) } } override fun getCart(userId: String): Cart { return mapToCart(userId, flowCachedObject.get()) }}
Key Implementation Details
FlowCachedObject: Provides reactive caching that emits updates when data changesUser-scoped storage: Stores carts in a map keyed by user ID, supporting multiple usersAutomatic removal: Setting quantity to 0 removes the item from cacheDTO mapping: Converts between domain models and cache DTOs
package com.denisbrandi.androidrealca.cart.data.modelimport kotlinx.serialization.*@Serializabledata class JsonCartCacheDto( @SerialName("usersWishlist") val usersCart: Map<String, List<JsonCartItemCacheDto>>)@Serializabledata class JsonCartItemCacheDto( @SerialName("id") val id: String, @SerialName("name") val name: String, @SerialName("price") val price: Double, @SerialName("currency") val currency: String, @SerialName("imageUrl") val imageUrl: String, @SerialName("quantity") val quantity: Int)