service-singleton-delegates-to-bulk
Service singleton CRUD operations must delegate to a bulk primitive.
Why
A singleton-only API forces callers into serial operations. Separate singleton and bulk implementations duplicate filtering, validation and mutation behavior; making bulk loop through singleton preserves that cost and can recurse when the singleton is later made an adapter.
Fix
Implement get_many, create_many, insert_many, upsert_many, update_many or delete_many as the primitive. Delegate the matching singleton through a one-item collection, forwarding shared context and awaiting async operations. Preserve result projection, missing-item, authorization and transaction semantics; use a reasoned exact-code suppression for a boundary that cannot support batching.
Examples
class UserService: async def get(self, key: str): return await self.store.get(key)class UserService: async def get(self, key: str): rows = await self.get_many([key]) return rows[0] if rows else None
async def get_many(self, keys: list[str]): return await self.store.get_many(keys)class UserService: def upsert(self, user): return self.upsert_many([user])[0]
def upsert_many(self, users): return [self.upsert(user) for user in users]class UserService: def upsert(self, user): return self.upsert_many([user])[0]
def upsert_many(self, users): return self.store.upsert_many(users)