The concern of managing user group membership should be factored out of the User object (and the UserGroupMembership class), into a separate service.
The new UserGroupStore service should have the following low-level methods:
```lang=php
newGroupMembership( UserIdentity $user, $group, $expiry = null ): UserGroupMembership;
addGroup( UserIdentity $user, $group )
removeGroup( UserIdentity $user, $group )
# needs knowledge of former groups table
listGroupNames( UserIdentity $user ): string[]
listGroupMemberships( UserIdentity $user ): GroupMembership[]
# cached, needs to check expiry
# need batch modefor listusers
purgeExpired()
getFormerGroups()
listDefinedGroups()
addUserGroupChangeListener( callable $listener )
# called from wiring
```
There should probably be a narrow interface for checking a user's group membership, e.g. UserGroupLookup that just contains listGroupNames and maybe something like userIsInGroup(). PermissionManager should consume that narrow interface, and should not have access to methods for changing group membership.
To allow other services (like PermissionManager) to react to changes in user groups, UserGroupStore needs a way to register listeners:
* `addUserGroupChangeListener( callable $listener )`
Wiring code, not the services themselves, are responsible for registering the necessary callbacks.
In addition, more high level services could be created that offers the following methods:
```
AutoPromotionManager (refactoring Autopromote):
addAutopromoteOnceGroups
EffectiveUserGroupLookup:
getEffectiveGroups
# needed on page view, because we expose via JS
# cache in session?
```