Last active
August 12, 2021 15:01
-
-
Save elnygren/203fc75281008f8b5f1f0f2cfa7a7161 to your computer and use it in GitHub Desktop.
Functional Dependency Injection in TypeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# dataclass saves the trouble of writing __init__ implementations | |
# frozen=True makes the class instances immutable (so no hidden mutable state) | |
@dataclass(frozen=True) | |
class UserConnector: | |
def get_user(id: str) -> User: | |
return User.objects.get(id=id) | |
@dataclass(frozen=True) | |
class OrgConnector: | |
def get_org(id: str) -> Org: | |
return Org.objects.get(id=id) | |
@dataclass(frozen=True) | |
class UserService: | |
# we can pass default implementations here because they are immutable | |
# (would be dangerous if not as these would become shared for all instances of UserService) | |
user_connector: UserConnector = UserConnector() | |
org_connector: OrgConnector = OrgConnector() | |
def get_user_with_org(self, id: str) -> User: | |
return { | |
'user': self.user_connector.get_user(id), | |
'org': self.org_connector.get_org(id) | |
} | |
if __name__ == '__main__': | |
# can be parametrised with Mock user_connector and org_connector in tests | |
s = UserService() | |
user = s.get_user_with_org("123") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Connector types (someone might call these DAO) | |
type UserConnector = { | |
getUser: (id: string) => User | |
} | |
type OrgConnector = { | |
getOrg: (id: string) => Org | |
} | |
// Dependencies for User Service | |
type UserServiceDeps = { | |
userConnector: UserConnector | |
orgConnector: OrgConnector | |
} | |
// "Factory" for user service, just use a closure+object literal (really more like a Record type here) | |
const makeUserService = ({ userConnector, orgConnector }: UserServiceDeps) => ({ | |
getUserWithOrg() { | |
return { | |
user: userConnector.getUser(), | |
org: orgConnector.getOrg() | |
} | |
}, | |
getUser() { | |
return userConnector.getUser() | |
} | |
}) | |
function main() { | |
// these are probably in a different file, fancier, can also be parametrised | |
const userConnector = { getUser: (id) => { db.select('user').where({id}) }} | |
const orgConnector = { getOrg: (id) => { db.select('organisation').where({id}) }} | |
// build UserService, inject connectors | |
const UserService = makeUserService({ userConnector, orgConnector }) | |
const user = UserService.getUserWithOrg() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment