Repository: hikari-py/hikari
Pull request #582 (2021-05-06 00:06:27)

Move away from hiding cache calls behind properties
### Summary
Move away from hiding cache calls behind properties
* This mainly applies to events (plus Guild, Member and Message)
* Add in "app" properties to applicable event objects to replace the app attrs field
* rename some cache getting properties to `get_{original_name}`

Since previously we've already had the standard of having cache calls behing get_x methods with the idea that a property/instance variable should remain relatively constant while the results of get methods has no guarantee of being constant, this is being used in this PR. The idea here is to make it more explicit when a cache call is being made vs when a field being present is dependent on API cases within the scope of the object that's currently being operated on to help keep semantics simple and explicit. This also helps avoid issues where a seemingly simple property may be making multiple non-lazy cache calls to build a dict where multiple accesses to the property would lead to extra unnecessary work being done.

This also has a fix for an internal bug which likely stemmed from this approach of hiding cache calls behind properties

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->


FasterSpeeding: This PR skips out some message and message event methods which will likely be changed in a bigger refactor of the message objects structure
FasterSpeeding: Pretty sure you missed Bot.me

Patch:
diff --git a/examples/image_resources/image_resources.py b/examples/image_resources/image_resources.py
index d3932871..048925b7 100644
--- a/examples/image_resources/image_resources.py
+++ b/examples/image_resources/image_resources.py
@@ -64,15 +64,15 @@ async def inspect_image(event: hikari.GuildMessageCreateEvent, what: str) -> Non
 
     # Show the guild icon:
     elif what.casefold() in ("guild", "server", "here", "this"):
-        if event.guild is None:
+        guild = event.get_guild()
+        if guild is None:
             await event.message.respond("Guild is missing from the cache :(")
             return
 
-        icon = event.guild.icon_url
-        if icon is None:
+        if guild.icon_url is None:
             await event.message.respond("This guild doesn't have an icon")
         else:
-            await event.message.respond("Guild icon", attachment=icon)
+            await event.message.respond("Guild icon", attachment=guild.icon_url)
 
     # Show the image for the given emoji if there is some content present:
     elif what:
diff --git a/hikari/channels.py b/hikari/channels.py
index 064806aa..de508a3d 100644
--- a/hikari/channels.py
+++ b/hikari/channels.py
@@ -224,8 +224,7 @@ async def fetch_webhook(self) -> webhooks.ChannelFollowerWebhook:
         assert isinstance(webhook, webhooks.ChannelFollowerWebhook)
         return webhook
 
-    @property
-    def channel(self) -> typing.Union[GuildNewsChannel, GuildTextChannel, None]:
+    def get_channel(self) -> typing.Union[GuildNewsChannel, GuildTextChannel, None]:
         """Get the channel being followed from the cache.
 
         !!! warning
diff --git a/hikari/errors.py b/hikari/errors.py
index f3ee80d7..864660ee 100644
--- a/hikari/errors.py
+++ b/hikari/errors.py
@@ -169,9 +169,7 @@ class ShardCloseCode(int, enums.Enum):
     @property
     def is_standard(self) -> bool:
         """Return `builtins.True` if this is a standard code."""
-        # Appears to be some MyPy bug where == is expected to
-        # return anything.
-        return bool((self.value // 1000) == 1)
+        return (self.value // 1000) == 1
 
 
 @attr.define(auto_exc=True, repr=False, weakref_slot=False)
diff --git a/hikari/events/channel_events.py b/hikari/events/channel_events.py
index a219ad4a..b605ef54 100644
--- a/hikari/events/channel_events.py
+++ b/hikari/events/channel_events.py
@@ -141,8 +141,7 @@ def guild_id(self) -> snowflakes.Snowflake:
             The ID of the guild that relates to this event.
         """
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
         """Get the cached guild that this event relates to, if known.
 
         If not, return `builtins.None`.
@@ -190,8 +189,7 @@ async def fetch_guild(self) -> guilds.RESTGuild:
         """
         return await self.app.rest.fetch_guild(self.guild_id)
 
-    @property
-    def channel(self) -> typing.Optional[channels.GuildChannel]:
+    def get_channel(self) -> typing.Optional[channels.GuildChannel]:
         """Get the cached channel that this event relates to, if known.
 
         If not, return `builtins.None`.
@@ -300,6 +298,11 @@ class ChannelCreateEvent(ChannelEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.channel.app
+
     @property
     @abc.abstractmethod
     def channel(self) -> channels.PartialChannel:
@@ -323,9 +326,6 @@ def channel_id(self) -> snowflakes.Snowflake:
 class GuildChannelCreateEvent(GuildChannelEvent, ChannelCreateEvent):
     """Event fired when a guild channel is created."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -350,6 +350,11 @@ class ChannelUpdateEvent(ChannelEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.channel.app
+
     @property
     @abc.abstractmethod
     def channel(self) -> channels.PartialChannel:
@@ -373,9 +378,6 @@ def channel_id(self) -> snowflakes.Snowflake:
 class GuildChannelUpdateEvent(GuildChannelEvent, ChannelUpdateEvent):
     """Event fired when a guild channel is edited."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -406,6 +408,11 @@ class ChannelDeleteEvent(ChannelEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.channel.app
+
     @property
     @abc.abstractmethod
     def channel(self) -> channels.PartialChannel:
@@ -434,9 +441,6 @@ async def fetch_channel(self) -> typing.NoReturn:
 class GuildChannelDeleteEvent(GuildChannelEvent, ChannelDeleteEvent):
     """Event fired when a guild channel is deleted."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -523,11 +527,9 @@ class GuildPinsUpdateEvent(PinsUpdateEvent, GuildChannelEvent):
     # <<inherited docstring from GuildChannelEvent>>.
 
     last_pin_timestamp: typing.Optional[datetime.datetime] = attr.field(repr=True)
-
     # <<inherited docstring from ChannelPinsUpdateEvent>>.
 
-    @property
-    def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
+    def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         """Get the cached channel that this event relates to, if known.
 
         If not, return `builtins.None`.
@@ -538,10 +540,7 @@ def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
             The cached channel this event relates to. If not known, this
             will return `builtins.None` instead.
         """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        channel = self.app.cache.get_guild_channel(self.channel_id)
+        channel = super().get_channel()
         assert channel is None or isinstance(channel, channels.TextableGuildChannel)
         return channel
 
@@ -687,9 +686,6 @@ async def fetch_invite(self) -> invites.Invite:
 class InviteCreateEvent(InviteEvent):
     """Event fired when an invite is created in a channel."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -702,6 +698,11 @@ class InviteCreateEvent(InviteEvent):
         The created invite object.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.invite.app
+
     @property
     def channel_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from ChannelEvent>>.
@@ -710,7 +711,7 @@ def channel_id(self) -> snowflakes.Snowflake:
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from GuildChannelEvent>>.
-        # This will always be non-None for guild channel invites.
+        # This will never be None for guild channel invites.
         assert self.invite.guild_id is not None
         return self.invite.guild_id
 
diff --git a/hikari/events/guild_events.py b/hikari/events/guild_events.py
index ce56dce7..c97877eb 100644
--- a/hikari/events/guild_events.py
+++ b/hikari/events/guild_events.py
@@ -48,6 +48,7 @@
 import attr
 
 from hikari import intents
+from hikari import traits
 from hikari.events import base_events
 from hikari.events import shard_events
 from hikari.internal import attr_extensions
@@ -58,7 +59,6 @@
     from hikari import guilds
     from hikari import presences as presences_
     from hikari import snowflakes
-    from hikari import traits
     from hikari import users
     from hikari import voices
     from hikari.api import shard as gateway_shard
@@ -83,22 +83,6 @@ def guild_id(self) -> snowflakes.Snowflake:
             The ID of the guild that relates to this event.
         """
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
-        """Get the cached guild that this event relates to, if known.
-
-        If not known, this will return `builtins.None` instead.
-
-        Returns
-        -------
-        typing.Optional[hikari.guilds.GatewayGuild]
-            The guild this event relates to, or `builtins.None` if not known.
-        """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        return self.app.cache.get_available_guild(self.guild_id) or self.app.cache.get_unavailable_guild(self.guild_id)
-
     async def fetch_guild(self) -> guilds.RESTGuild:
         """Perform an API call to get the guild that this event relates to.
 
@@ -119,6 +103,21 @@ async def fetch_guild_preview(self) -> guilds.GuildPreview:
         """
         return await self.app.rest.fetch_guild_preview(self.guild_id)
 
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
+        """Get the cached guild that this event relates to, if known.
+
+        If not known, this will return `builtins.None` instead.
+
+        Returns
+        -------
+        typing.Optional[hikari.guilds.GatewayGuild]
+            The guild this event relates to, or `builtins.None` if not known.
+        """
+        if not isinstance(self.app, traits.CacheAware):
+            return None
+
+        return self.app.cache.get_available_guild(self.guild_id) or self.app.cache.get_unavailable_guild(self.guild_id)
+
 
 @base_events.requires_intents(intents.Intents.GUILDS)
 class GuildVisibilityEvent(GuildEvent, abc.ABC):
@@ -147,9 +146,6 @@ class GuildAvailableEvent(GuildVisibilityEvent):
         event models.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -230,6 +226,11 @@ class GuildAvailableEvent(GuildVisibilityEvent):
         The nonce used to request the member chunks.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.guild.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from GuildEvent>>.
@@ -282,9 +283,6 @@ class GuildUnavailableEvent(GuildVisibilityEvent):
 class GuildUpdateEvent(GuildEvent):
     """Event fired when an existing guild is updated."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -321,6 +319,11 @@ class GuildUpdateEvent(GuildEvent):
         The roles in the guild.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.guild.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from GuildEvent>>.
@@ -333,6 +336,11 @@ class BanEvent(GuildEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.user.app
+
     @property
     @abc.abstractmethod
     def user(self) -> users.User:
@@ -361,9 +369,6 @@ async def fetch_user(self) -> users.User:
 class BanCreateEvent(BanEvent):
     """Event that is fired when a user is banned from a guild."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -404,9 +409,6 @@ async def fetch_ban(self) -> guilds.GuildBan:
 class BanDeleteEvent(BanEvent):
     """Event that is fired when a user is unbanned from a guild."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -606,9 +608,6 @@ class PresenceUpdateEvent(shard_events.ShardEvent):
     shards that saw the presence update.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -643,6 +642,11 @@ class PresenceUpdateEvent(shard_events.ShardEvent):
         The partial user containing the updated fields.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.presence.app
+
     @property
     def user_id(self) -> snowflakes.Snowflake:
         """User ID of the user that updated their presence.
diff --git a/hikari/events/member_events.py b/hikari/events/member_events.py
index 098636ad..c6f1d5a5 100644
--- a/hikari/events/member_events.py
+++ b/hikari/events/member_events.py
@@ -54,6 +54,11 @@ class MemberEvent(shard_events.ShardEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.user.app
+
     @property
     @abc.abstractmethod
     def guild_id(self) -> snowflakes.Snowflake:
@@ -87,8 +92,7 @@ def user_id(self) -> snowflakes.Snowflake:
         """
         return self.user.id
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
         """Get the cached view of the guild this member event occurred in.
 
         If the guild itself is not cached, this will return `builtins.None`.
@@ -111,9 +115,6 @@ def guild(self) -> typing.Optional[guilds.GatewayGuild]:
 class MemberCreateEvent(MemberEvent):
     """Event that is fired when a member joins a guild."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -146,9 +147,6 @@ class MemberUpdateEvent(MemberEvent):
     This may occur if roles are amended, or if the nickname is changed.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -184,9 +182,6 @@ def user(self) -> users.User:
 class MemberDeleteEvent(MemberEvent):
     """Event fired when a member is kicked from or leaves a guild."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
diff --git a/hikari/events/message_events.py b/hikari/events/message_events.py
index d5959623..93b333c0 100644
--- a/hikari/events/message_events.py
+++ b/hikari/events/message_events.py
@@ -94,6 +94,11 @@ class MessageCreateEvent(MessageEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.message.app
+
     @property
     def author(self) -> users.User:
         """User that sent the message.
@@ -212,9 +217,6 @@ class GuildMessageCreateEvent(MessageCreateEvent):
     This contains the full message in the internal `message` attribute.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>
-
     message: messages.Message = attr.field()
     # <<inherited docstring from MessageCreateEvent>>
 
@@ -245,7 +247,20 @@ def member(self) -> typing.Optional[guilds.Member]:
         return self.message.member
 
     @property
-    def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
+    def guild_id(self) -> snowflakes.Snowflake:
+        """ID of the guild that this event occurred in.
+
+        Returns
+        -------
+        hikari.snowflakes.Snowflake
+            The ID of the guild that this event occurred in.
+        """
+        guild_id = self.message.guild_id
+        # Always present on guild events
+        assert isinstance(guild_id, snowflakes.Snowflake), "no guild_id attribute set"
+        return guild_id
+
+    def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         """Channel that the message was sent in, if known.
 
         Returns
@@ -263,8 +278,7 @@ def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         ), f"Cached channel ID is not a TextableGuildChannel, but a {type(channel).__name__}!"
         return channel
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
         """Get the cached guild that this event occurred in, if known.
 
         !!! note
@@ -282,19 +296,18 @@ def guild(self) -> typing.Optional[guilds.GatewayGuild]:
 
         return self.app.cache.get_guild(self.guild_id)
 
-    @property
-    def guild_id(self) -> snowflakes.Snowflake:
-        """ID of the guild that this event occurred in.
+    def get_member(self) -> typing.Optional[guilds.Member]:
+        """Get the member that sent this message from the cache if available.
 
         Returns
         -------
-        hikari.snowflakes.Snowflake
-            The ID of the guild that this event occurred in.
+        typing.Optional[hikari.guilds.Member]
+            Cached object of the member that sent the message if found.
         """
-        guild_id = self.message.guild_id
-        # Always present on guild events
-        assert isinstance(guild_id, snowflakes.Snowflake), "no guild_id attribute set"
-        return guild_id
+        if isinstance(self.app, traits.CacheAware):
+            return self.app.cache.get_member(self.guild_id, self.message.author.id)
+
+        return None
 
 
 @attr_extensions.with_copy
@@ -306,9 +319,6 @@ class DMMessageCreateEvent(MessageCreateEvent):
     This contains the full message in the internal `message` attribute.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>
-
     message: messages.Message = attr.field()
     # <<inherited docstring from MessageCreateEvent>>
 
@@ -327,6 +337,11 @@ class MessageUpdateEvent(MessageEvent, abc.ABC):
 
     __slots__: typing.Sequence[str] = ()
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.message.app
+
     @property
     def author(self) -> typing.Optional[users.User]:
         """User that sent the message.
@@ -472,9 +487,6 @@ class GuildMessageUpdateEvent(MessageUpdateEvent):
         due to Discord limitations.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>
-
     old_message: typing.Optional[messages.PartialMessage] = attr.field()
     """The old message object.
 
@@ -504,36 +516,43 @@ def author(self) -> typing.Optional[users.User]:
 
     @property
     def member(self) -> typing.Optional[guilds.Member]:
-        """Member that sent the message.
-
-        Returns
-        -------
-        typing.Optional[hikari.guilds.Member]
-            The member that sent the message. If the member is cached
-            (the intents are enabled), then this will be the corresponding
-            member object instead (which is a specialization of the
-            user object you should otherwise expect).
+        """Member that sent the message if provided by the event.
 
+        !!! note
             This will be `builtins.None` in some cases, such as when Discord
             updates a message with an embed for a URL preview or if the message
             was sent by a webhook.
         """
-        member = self.message.member
-        if member is not None:
-            return member
-
-        author = self.message.author
+        return self.message.member
 
-        if author is not None and isinstance(self.app, traits.CacheAware):
-            member = self.app.cache.get_member(self.guild_id, author.id)
+    def get_member(self) -> typing.Optional[guilds.Member]:
+        """Get the member that sent this message from the cache if available.
 
-            if member is not None:
-                return member
+        Returns
+        -------
+        typing.Optional[hikari.guilds.Member]
+            Cached object of the member that sent the message if found.
+        """
+        if self.message.author is not None and isinstance(self.app, traits.CacheAware):
+            return self.app.cache.get_member(self.guild_id, self.message.author.id)
 
         return None
 
     @property
-    def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
+    def guild_id(self) -> snowflakes.Snowflake:
+        """ID of the guild that this event occurred in.
+
+        Returns
+        -------
+        hikari.snowflakes.Snowflake
+            The ID of the guild that this event occurred in.
+        """
+        guild_id = self.message.guild_id
+        # Always present on guild events
+        assert isinstance(guild_id, snowflakes.Snowflake), f"expected guild_id, got {guild_id}"
+        return guild_id
+
+    def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         """Channel that the message was sent in, if known.
 
         Returns
@@ -551,8 +570,7 @@ def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         ), f"Cached channel ID is not a TextableGuildChannel, but a {type(channel).__name__}!"
         return channel
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
         """Get the cached guild that this event occurred in, if known.
 
         !!! note
@@ -570,20 +588,6 @@ def guild(self) -> typing.Optional[guilds.GatewayGuild]:
 
         return self.app.cache.get_guild(self.guild_id)
 
-    @property
-    def guild_id(self) -> snowflakes.Snowflake:
-        """ID of the guild that this event occurred in.
-
-        Returns
-        -------
-        hikari.snowflakes.Snowflake
-            The ID of the guild that this event occurred in.
-        """
-        guild_id = self.message.guild_id
-        # Always present on guild events
-        assert isinstance(guild_id, snowflakes.Snowflake), f"expected guild_id, got {guild_id}"
-        return guild_id
-
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
@@ -596,9 +600,6 @@ class DMMessageUpdateEvent(MessageUpdateEvent):
         due to Discord limitations.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>
-
     old_message: typing.Optional[messages.PartialMessage] = attr.field()
     """The old message object.
 
@@ -707,8 +708,7 @@ class GuildMessageDeleteEvent(MessageDeleteEvent):
     shard: shard_.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>
 
-    @property
-    def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
+    def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         """Get the cached channel the messages were sent in, if known.
 
         Returns
@@ -726,8 +726,7 @@ def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
         ), f"Cached channel ID is not a TextableGuildChannel, but a {type(channel).__name__}!"
         return channel
 
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
         """Get the cached guild this event corresponds to, if known.
 
         !!! note
diff --git a/hikari/events/reaction_events.py b/hikari/events/reaction_events.py
index b0ff4d33..797139e4 100644
--- a/hikari/events/reaction_events.py
+++ b/hikari/events/reaction_events.py
@@ -208,9 +208,6 @@ def emoji(self) -> emojis.Emoji:
 class GuildReactionAddEvent(GuildReactionEvent, ReactionAddEvent):
     """Event fired when a reaction is added to a guild message."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -230,9 +227,13 @@ class GuildReactionAddEvent(GuildReactionEvent, ReactionAddEvent):
     # <<inherited docstring from ReactionEvent>>.
 
     emoji: emojis.Emoji = attr.field()
-
     # <<inherited docstring from ReactionAddEvent>>.
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.member.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from GuildReactionEvent>>.
diff --git a/hikari/events/role_events.py b/hikari/events/role_events.py
index 8447a547..d844f5e8 100644
--- a/hikari/events/role_events.py
+++ b/hikari/events/role_events.py
@@ -82,9 +82,6 @@ def role_id(self) -> snowflakes.Snowflake:
 class RoleCreateEvent(RoleEvent):
     """Event fired when a role is created."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -97,6 +94,11 @@ class RoleCreateEvent(RoleEvent):
         The created role.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.role.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from RoleEvent>>.
@@ -114,9 +116,6 @@ def role_id(self) -> snowflakes.Snowflake:
 class RoleUpdateEvent(RoleEvent):
     """Event fired when a role is updated."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -135,6 +134,11 @@ class RoleUpdateEvent(RoleEvent):
         The created role.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.role.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from RoleEvent>>.
diff --git a/hikari/events/shard_events.py b/hikari/events/shard_events.py
index 5ee76d25..ffadea50 100644
--- a/hikari/events/shard_events.py
+++ b/hikari/events/shard_events.py
@@ -132,9 +132,6 @@ class ShardDisconnectedEvent(ShardStateEvent):
 class ShardReadyEvent(ShardStateEvent):
     """Event fired when a shard declares it is ready."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<docstring inherited from ShardEvent>>.
 
@@ -195,6 +192,11 @@ class ShardReadyEvent(ShardStateEvent):
         The current application's flags.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.my_user.app
+
 
 @attr_extensions.with_copy
 @attr.define(kw_only=True, weakref_slot=False)
diff --git a/hikari/events/typing_events.py b/hikari/events/typing_events.py
index c0367bd2..5b9ad41e 100644
--- a/hikari/events/typing_events.py
+++ b/hikari/events/typing_events.py
@@ -90,28 +90,31 @@ def timestamp(self) -> datetime.datetime:
             UTC timestamp of when the user started typing.
         """
 
-    @property
-    @abc.abstractmethod
-    def user(self) -> typing.Optional[users.User]:
-        """Get the cached user that is typing, if known.
+    async def fetch_channel(self) -> channels.TextableChannel:
+        """Perform an API call to fetch an up-to-date image of this channel.
 
         Returns
         -------
-        typing.Optional[hikari.users.User]
-            The user, if known.
+        hikari.channels.TextableChannel
+            The channel.
         """
+        channel = await self.app.rest.fetch_channel(self.channel_id)
+        assert isinstance(channel, channels.TextableChannel)
+        return channel
 
-    @abc.abstractmethod
-    async def fetch_channel(self) -> channels.TextableChannel:
-        """Perform an API call to fetch an up-to-date image of this channel.
+    def get_user(self) -> typing.Optional[users.User]:
+        """Get the cached user that is typing, if known.
 
         Returns
         -------
-        hikari.channels.TextableChannel
-            The channel.
+        typing.Optional[hikari.users.User]
+            The user, if known.
         """
+        if isinstance(self.app, traits.CacheAware):
+            return self.app.cache.get_user(self.user_id)
+
+        return None
 
-    @abc.abstractmethod
     async def fetch_user(self) -> users.User:
         """Perform an API call to fetch an up-to-date image of this user.
 
@@ -119,7 +122,28 @@ async def fetch_user(self) -> users.User:
         -------
         hikari.users.User
             The user.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.NotFoundError
+            If the user is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
         """
+        return await self.app.rest.fetch_user(self.user_id)
 
     def trigger_typing(self) -> special_endpoints.TypingIndicator:
         """Return a typing indicator for this channel that can be awaited.
@@ -139,9 +163,6 @@ def trigger_typing(self) -> special_endpoints.TypingIndicator:
 class GuildTypingEvent(TypingEvent):
     """Event fired when a user starts typing in a guild channel."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -160,56 +181,24 @@ class GuildTypingEvent(TypingEvent):
         The ID of the guild that relates to this event.
     """
 
-    user: guilds.Member = attr.field(repr=False)
-    """Member object of the user who triggered this typing event.
-
-    Unlike on `PrivateTypingEvent` instances, Discord will always send
-    this field in any payload.
+    member: guilds.Member = attr.field(repr=False)
+    """Object of the member who triggered this typing event.
 
     Returns
     -------
     hikari.guilds.Member
-        Member of the user who triggered this typing event.
+        Object of the member who triggered this typing event.
     """
 
     @property
-    def channel(self) -> typing.Optional[channels.TextableGuildChannel]:
-        """Get the cached channel object this typing event occurred in.
-
-        Returns
-        -------
-        typing.Optional[hikari.channels.TextableGuildChannel]
-            The channel.
-        """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        channel = self.app.cache.get_guild_channel(self.channel_id)
-        assert channel is None or isinstance(
-            channel, channels.TextableGuildChannel
-        ), f"expected TextableGuildChannel from cache, got {channel}"
-        return channel
-
-    @property
-    def guild(self) -> typing.Optional[guilds.GatewayGuild]:
-        """Get the cached object of the guild this typing event occurred in.
-
-        If the guild is not found then this will return `builtins.None`.
-
-        Returns
-        -------
-        typing.Optional[hikari.guilds.GatewayGuild]
-            The object of the gateway guild if found else `builtins.None`.
-        """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        return self.app.cache.get_available_guild(self.guild_id) or self.app.cache.get_unavailable_guild(self.guild_id)
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.member.app
 
     @property
     def user_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from TypingEvent>>.
-        return self.user.id
+        return self.member.id
 
     async def fetch_channel(self) -> typing.Union[channels.TextableGuildChannel]:
         """Perform an API call to fetch an up-to-date image of this channel.
@@ -219,7 +208,7 @@ async def fetch_channel(self) -> typing.Union[channels.TextableGuildChannel]:
         typing.Union[hikari.channels.TextableGuildChannel]
             The channel.
         """
-        channel = await self.app.rest.fetch_channel(self.channel_id)
+        channel = await super().fetch_channel()
         assert isinstance(
             channel, channels.TextableGuildChannel
         ), f"expected TextableGuildChannel from API, got {channel}"
@@ -245,8 +234,8 @@ async def fetch_guild_preview(self) -> guilds.GuildPreview:
         """
         return await self.app.rest.fetch_guild_preview(self.guild_id)
 
-    async def fetch_user(self) -> guilds.Member:
-        """Perform an API call to fetch an up-to-date image of this member.
+    async def fetch_member(self) -> guilds.Member:
+        """Perform an API call to fetch an up-to-date image of this event's member.
 
         Returns
         -------
@@ -255,6 +244,38 @@ async def fetch_user(self) -> guilds.Member:
         """
         return await self.app.rest.fetch_member(self.guild_id, self.user_id)
 
+    def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
+        """Get the cached channel object this typing event occurred in.
+
+        Returns
+        -------
+        typing.Optional[hikari.channels.TextableGuildChannel]
+            The channel.
+        """
+        if not isinstance(self.app, traits.CacheAware):
+            return None
+
+        channel = self.app.cache.get_guild_channel(self.channel_id)
+        assert channel is None or isinstance(
+            channel, channels.TextableGuildChannel
+        ), f"expected TextableGuildChannel from cache, got {channel}"
+        return channel
+
+    def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
+        """Get the cached object of the guild this typing event occurred in.
+
+        If the guild is not found then this will return `builtins.None`.
+
+        Returns
+        -------
+        typing.Optional[hikari.guilds.GatewayGuild]
+            The object of the gateway guild if found else `builtins.None`.
+        """
+        if not isinstance(self.app, traits.CacheAware):
+            return None
+
+        return self.app.cache.get_available_guild(self.guild_id) or self.app.cache.get_unavailable_guild(self.guild_id)
+
 
 @base_events.requires_intents(intents.Intents.DM_MESSAGES)
 @attr_extensions.with_copy
@@ -277,14 +298,6 @@ class DMTypingEvent(TypingEvent):
     timestamp: datetime.datetime = attr.field(repr=False)
     # <<inherited docstring from TypingEvent>>.
 
-    @property
-    def user(self) -> typing.Optional[users.User]:
-        # <<inherited docstring from TypingEvent>>.
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        return self.app.cache.get_user(self.user_id)
-
     async def fetch_channel(self) -> channels.DMChannel:
         """Perform an API call to fetch an up-to-date image of this channel.
 
@@ -318,36 +331,6 @@ async def fetch_channel(self) -> channels.DMChannel:
         hikari.errors.InternalServerError
             If an internal error occurs on Discord while handling the request.
         """
-        channel = await self.app.rest.fetch_channel(self.channel_id)
+        channel = await super().fetch_channel()
         assert isinstance(channel, channels.DMChannel), f"expected DMChannel from API, got {channel}"
         return channel
-
-    async def fetch_user(self) -> users.User:
-        """Perform an API call to fetch an up-to-date image of the user.
-
-        Returns
-        -------
-        hikari.users.User
-            The user.
-
-        Raises
-        ------
-        hikari.errors.UnauthorizedError
-            If you are unauthorized to make the request (invalid/missing token).
-        hikari.errors.NotFoundError
-            If the user is not found.
-        hikari.errors.RateLimitTooLongError
-            Raised in the event that a rate limit occurs that is
-            longer than `max_rate_limit` when making a request.
-        hikari.errors.RateLimitedError
-            Usually, Hikari will handle and retry on hitting
-            rate-limits automatically. This includes most bucket-specific
-            rate-limits and global rate-limits. In some rare edge cases,
-            however, Discord implements other undocumented rules for
-            rate-limiting, such as limits per attribute. These cannot be
-            detected or handled normally by Hikari due to their undocumented
-            nature, and will trigger this exception if they occur.
-        hikari.errors.InternalServerError
-            If an internal error occurs on Discord while handling the request.
-        """
-        return await self.app.rest.fetch_user(self.user_id)
diff --git a/hikari/events/user_events.py b/hikari/events/user_events.py
index 22325109..ec2593f8 100644
--- a/hikari/events/user_events.py
+++ b/hikari/events/user_events.py
@@ -43,9 +43,6 @@
 class OwnUserUpdateEvent(shard_events.ShardEvent):
     """Event fired when the account user is updated."""
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring from ShardEvent>>.
 
@@ -63,3 +60,8 @@ class OwnUserUpdateEvent(shard_events.ShardEvent):
     hikari.users.OwnUser
         This application user.
     """
+
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.user.app
diff --git a/hikari/events/voice_events.py b/hikari/events/voice_events.py
index 249843a2..5f65e58f 100644
--- a/hikari/events/voice_events.py
+++ b/hikari/events/voice_events.py
@@ -76,9 +76,6 @@ class VoiceStateUpdateEvent(VoiceEvent):
     to connect to the voice gateway to stream audio or video content.
     """
 
-    app: traits.RESTAware = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
-    # <<inherited docstring from Event>>.
-
     shard: gateway_shard.GatewayShard = attr.field(metadata={attr_extensions.SKIP_DEEP_COPY: True})
     # <<inherited docstring>>.
 
@@ -97,6 +94,11 @@ class VoiceStateUpdateEvent(VoiceEvent):
         The voice state that was updated.
     """
 
+    @property
+    def app(self) -> traits.RESTAware:
+        # <<inherited docstring from Event>>.
+        return self.state.app
+
     @property
     def guild_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from VoiceEvent>>
diff --git a/hikari/guilds.py b/hikari/guilds.py
index ccb38170..75377d26 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -54,7 +54,6 @@
     "WelcomeChannel",
 ]
 
-import abc
 import typing
 
 import attr
@@ -462,8 +461,7 @@ def mention(self) -> str:
         """
         return f"<@!{self.id}>" if self.nickname is not None else self.user.mention
 
-    @property
-    def presence(self) -> typing.Optional[presences_.MemberPresence]:
+    def get_presence(self) -> typing.Optional[presences_.MemberPresence]:
         """Get the cached presence for this member, if known.
 
         Presence info includes user status and activities.
@@ -480,8 +478,7 @@ def presence(self) -> typing.Optional[presences_.MemberPresence]:
 
         return self.user.app.cache.get_presence(self.guild_id, self.user.id)
 
-    @property
-    def roles(self) -> typing.Sequence[Role]:
+    def get_roles(self) -> typing.Sequence[Role]:
         """Return the roles the user has.
 
         This will be empty if the roles are missing from the cache.
@@ -491,15 +488,18 @@ def roles(self) -> typing.Sequence[Role]:
         typing.Sequence[hikari.guilds.Role]
             The roles the users has.
         """
+        roles: typing.List[Role] = []
+
         if not isinstance(self.user.app, traits.CacheAware):
-            return []
+            return roles
 
-        roles_view = self.user.app.cache.get_roles_view_for_guild(self.guild_id)
+        for role_id in self.role_ids:
+            if role := self.user.app.cache.get_role(role_id):
+                roles.append(role)
 
-        return [r for r in roles_view.values() if r.id in self.role_ids]
+        return roles
 
-    @property
-    def top_role(self) -> typing.Optional[Role]:
+    def get_top_role(self) -> typing.Optional[Role]:
         """Return the highest role the member has.
 
         Returns
@@ -508,7 +508,7 @@ def top_role(self) -> typing.Optional[Role]:
             `builtins.None` if the cache is missing the roles information or
             the highest role the user has.
         """
-        roles = sorted(self.roles, key=lambda r: r.position, reverse=True)
+        roles = sorted(self.get_roles(), key=lambda r: r.position, reverse=True)
 
         try:
             return next(iter(roles))
@@ -2251,7 +2251,7 @@ def make_splash_url(self, *, ext: str = "png", size: int = 4096) -> typing.Optio
 
 
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
-class Guild(PartialGuild, abc.ABC):
+class Guild(PartialGuild):
     """A representation of a guild on Discord."""
 
     features: typing.Sequence[typing.Union[str, GuildFeature]] = attr.field(eq=False, hash=False, repr=False)
@@ -2403,7 +2403,11 @@ def discovery_splash_url(self) -> typing.Optional[files.URL]:
         return self.make_discovery_splash_url()
 
     @property
-    def members(self) -> typing.Mapping[snowflakes.Snowflake, Member]:
+    def splash_url(self) -> typing.Optional[files.URL]:
+        """Splash URL for the guild, if set."""
+        return self.make_splash_url()
+
+    def get_members(self) -> typing.Mapping[snowflakes.Snowflake, Member]:
         """Get the members cached for the guild.
 
         typing.Mapping[hikari.snowflakes.Snowflake, Member]
@@ -2414,8 +2418,7 @@ def members(self) -> typing.Mapping[snowflakes.Snowflake, Member]:
 
         return self.app.cache.get_members_view_for_guild(self.id)
 
-    @property
-    def presences(self) -> typing.Mapping[snowflakes.Snowflake, presences_.MemberPresence]:
+    def get_presences(self) -> typing.Mapping[snowflakes.Snowflake, presences_.MemberPresence]:
         """Get the presences cached for the guild.
 
         typing.Mapping[hikari.snowflakes.Snowflake, hikari.presences.MemberPresence]
@@ -2427,8 +2430,7 @@ def presences(self) -> typing.Mapping[snowflakes.Snowflake, presences_.MemberPre
 
         return self.app.cache.get_presences_view_for_guild(self.id)
 
-    @property
-    def channels(self) -> typing.Mapping[snowflakes.Snowflake, channels_.GuildChannel]:
+    def get_channels(self) -> typing.Mapping[snowflakes.Snowflake, channels_.GuildChannel]:
         """Get the channels cached for the guild.
 
         Returns
@@ -2442,8 +2444,7 @@ def channels(self) -> typing.Mapping[snowflakes.Snowflake, channels_.GuildChanne
 
         return self.app.cache.get_guild_channels_view_for_guild(self.id)
 
-    @property
-    def voice_states(self) -> typing.Mapping[snowflakes.Snowflake, voices_.VoiceState]:
+    def get_voice_states(self) -> typing.Mapping[snowflakes.Snowflake, voices_.VoiceState]:
         """Get the voice states cached for the guild.
 
         Returns
@@ -2457,9 +2458,7 @@ def voice_states(self) -> typing.Mapping[snowflakes.Snowflake, voices_.VoiceStat
 
         return self.app.cache.get_voice_states_view_for_guild(self.id)
 
-    @property
-    @abc.abstractmethod
-    def emojis(self) -> typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji]:
+    def get_emojis(self) -> typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji]:
         """Return the emojis in this guild.
 
         Returns
@@ -2467,10 +2466,12 @@ def emojis(self) -> typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoj
         typing.Mapping[hikari.snowflakes.Snowflake, hikari.emojis.KnownCustomEmoji]
             A mapping of emoji IDs to the objects of emojis in this guild.
         """
+        if not isinstance(self.app, traits.CacheAware):
+            return {}
 
-    @property
-    @abc.abstractmethod
-    def roles(self) -> typing.Mapping[snowflakes.Snowflake, Role]:
+        return self.app.cache.get_emojis_view_for_guild(self.id)
+
+    def get_roles(self) -> typing.Mapping[snowflakes.Snowflake, Role]:
         """Return the roles in this guild.
 
         Returns
@@ -2478,11 +2479,10 @@ def roles(self) -> typing.Mapping[snowflakes.Snowflake, Role]:
         typing.Mapping[hikari.snowflakes.Snowflake, Role]
             A mapping of role IDs to the objects of roles in this guild.
         """
+        if not isinstance(self.app, traits.CacheAware):
+            return {}
 
-    @property
-    def splash_url(self) -> typing.Optional[files.URL]:
-        """Splash URL for the guild, if set."""
-        return self.make_splash_url()
+        return self.app.cache.get_roles_view_for_guild(self.id)
 
     def make_banner_url(self, *, ext: str = "png", size: int = 4096) -> typing.Optional[files.URL]:
         """Generate the guild's banner image URL, if set.
@@ -2633,7 +2633,7 @@ def get_my_member(self) -> typing.Optional[Member]:
         if not isinstance(self.app, traits.ShardAware):
             return None
 
-        me = self.app.me
+        me = self.app.get_me()
         if me is None:
             return None
 
@@ -2679,15 +2679,44 @@ def get_voice_state(
 
         return self.app.cache.get_voice_state(self.id, user)
 
-    @abc.abstractmethod
     def get_emoji(
         self, emoji: snowflakes.SnowflakeishOr[emojis_.CustomEmoji]
     ) -> typing.Optional[emojis_.KnownCustomEmoji]:
-        """Get an emoji from the cache by it's ID."""
+        """Get a cached role that belongs to the guild by it's ID or object.
+
+        Parameters
+        ----------
+        emoji : hikari.snowflakes.SnowflakeishOr[hikari.emojis.CustomEmoji]
+            The object or ID of the emoji to get from the cache.
+
+        Returns
+        -------
+        typing.Optional[hikari.emojis.KnownCustomEmoji]
+            The object of the custom emoji if found in cache, else
+            `builtins.None`.
+        """
+        if not isinstance(self.app, traits.CacheAware):
+            return None
+
+        return self.app.cache.get_emoji(emoji)
 
-    @abc.abstractmethod
     def get_role(self, role: snowflakes.SnowflakeishOr[PartialRole]) -> typing.Optional[Role]:
-        """Get a role from the cache by it's ID."""
+        """Get a cached role that belongs to the guild by it's ID or object.
+
+        Parameters
+        ----------
+        role : hikari.snowflakes.SnowflakeishOr[PartialRole]
+            The object or ID of the role to get for this guild from the cache.
+
+        Returns
+        -------
+        typing.Optional[Role]
+            The object of the role found in cache, else `builtins.None`.
+        """
+        if not isinstance(self.app, traits.CacheAware):
+            return None
+
+        return self.app.cache.get_role(role)
 
     async def fetch_owner(self) -> Member:
         """Fetch the owner of the guild.
@@ -2932,14 +2961,12 @@ async def fetch_public_updates_channel(self) -> typing.Optional[channels_.GuildT
 class RESTGuild(Guild):
     """Guild specialization that is sent via the REST API only."""
 
-    # In a REST-provided guild, we provide these attributes directly, as the API will give them in the response.
-    # This is different to Gateway guilds which will perform a cache-hit to handle this usually.
-    _emojis: typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji] = attr.field(
+    emojis: typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji] = attr.field(
         eq=False, hash=False, repr=False
     )
     """A mapping of emoji IDs to the objects of the emojis this guild provides."""
 
-    _roles: typing.Mapping[snowflakes.Snowflake, Role] = attr.field(eq=False, hash=False, repr=False)
+    roles: typing.Mapping[snowflakes.Snowflake, Role] = attr.field(eq=False, hash=False, repr=False)
     """The roles in this guild, represented as a mapping of role ID to role object."""
 
     approximate_active_member_count: typing.Optional[int] = attr.field(eq=False, hash=False, repr=False)
@@ -2960,26 +2987,6 @@ class RESTGuild(Guild):
     max_members: int = attr.field(eq=False, hash=False, repr=False)
     """The maximum number of members allowed in this guild."""
 
-    @property
-    def emojis(self) -> typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji]:
-        # <<inherited docstring from Guild>>.
-        return self._emojis
-
-    @property
-    def roles(self) -> typing.Mapping[snowflakes.Snowflake, Role]:
-        # <<inherited docstring from Guild>>.
-        return self._roles
-
-    def get_emoji(
-        self, emoji: snowflakes.SnowflakeishOr[emojis_.CustomEmoji]
-    ) -> typing.Optional[emojis_.KnownCustomEmoji]:
-        # <<inherited docstring from Guild>>.
-        return self._emojis.get(snowflakes.Snowflake(emoji))
-
-    def get_role(self, role: snowflakes.SnowflakeishOr[PartialRole]) -> typing.Optional[Role]:
-        # <<inherited docstring from Guild>>.
-        return self._roles.get(snowflakes.Snowflake(role))
-
 
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
 class GatewayGuild(Guild):
@@ -3011,58 +3018,3 @@ class GatewayGuild(Guild):
     event. If the guild is received from any other place, this will always be
     `builtins.None`.
     """
-
-    @property
-    def emojis(self) -> typing.Mapping[snowflakes.Snowflake, emojis_.KnownCustomEmoji]:
-        # <<inherited docstring from Guild>>.
-        if not isinstance(self.app, traits.CacheAware):
-            return {}
-
-        return self.app.cache.get_emojis_view_for_guild(self.id)
-
-    @property
-    def roles(self) -> typing.Mapping[snowflakes.Snowflake, Role]:
-        # <<inherited docstring from Guild>>.
-        if not isinstance(self.app, traits.CacheAware):
-            return {}
-
-        return self.app.cache.get_roles_view_for_guild(self.id)
-
-    def get_emoji(
-        self, emoji: snowflakes.SnowflakeishOr[emojis_.CustomEmoji]
-    ) -> typing.Optional[emojis_.KnownCustomEmoji]:
-        """Get a cached role that belongs to the guild by it's ID or object.
-
-        Parameters
-        ----------
-        emoji : hikari.snowflakes.SnowflakeishOr[hikari.emojis.CustomEmoji]
-            The object or ID of the emoji to get from the cache.
-
-        Returns
-        -------
-        typing.Optional[hikari.emojis.KnownCustomEmoji]
-            The object of the custom emoji if found in cache, else
-            `builtins.None`.
-        """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        return self.app.cache.get_emoji(emoji)
-
-    def get_role(self, role: snowflakes.SnowflakeishOr[PartialRole]) -> typing.Optional[Role]:
-        """Get a cached role that belongs to the guild by it's ID or object.
-
-        Parameters
-        ----------
-        role : hikari.snowflakes.SnowflakeishOr[PartialRole]
-            The object or ID of the role to get for this guild from the cache.
-
-        Returns
-        -------
-        typing.Optional[Role]
-            The object of the role found in cache, else `builtins.None`.
-        """
-        if not isinstance(self.app, traits.CacheAware):
-            return None
-
-        return self.app.cache.get_role(role)
diff --git a/hikari/impl/bot.py b/hikari/impl/bot.py
index 4a6388a7..a0bf9bbe 100644
--- a/hikari/impl/bot.py
+++ b/hikari/impl/bot.py
@@ -330,10 +330,6 @@ def http_settings(self) -> config.HTTPSettings:
     def intents(self) -> intents_.Intents:
         return self._intents
 
-    @property
-    def me(self) -> typing.Optional[users_.OwnUser]:
-        return self._cache.get_me()
-
     @property
     def proxy_settings(self) -> config.ProxySettings:
         return self._proxy_settings
@@ -358,6 +354,9 @@ def _check_if_alive(self) -> None:
         if not self._is_alive:
             raise errors.ComponentStateConflictError("bot is not running so it cannot be interacted with")
 
+    def get_me(self) -> typing.Optional[users_.OwnUser]:
+        return self._cache.get_me()
+
     async def close(self) -> None:
         """Kill the application by shutting all components down."""
         self._check_if_alive()
diff --git a/hikari/impl/event_factory.py b/hikari/impl/event_factory.py
index 8a050acf..f582cea5 100644
--- a/hikari/impl/event_factory.py
+++ b/hikari/impl/event_factory.py
@@ -80,7 +80,7 @@ def deserialize_channel_create_event(
     ) -> channel_events.ChannelCreateEvent:
         channel = self._app.entity_factory.deserialize_channel(payload)
         if isinstance(channel, channel_models.GuildChannel):
-            return channel_events.GuildChannelCreateEvent(app=self._app, shard=shard, channel=channel)
+            return channel_events.GuildChannelCreateEvent(shard=shard, channel=channel)
         if isinstance(channel, channel_models.PrivateChannel):
             raise NotImplementedError("DM channel create events are undocumented behaviour")
         raise TypeError(f"Expected GuildChannel or PrivateChannel but received {type(channel).__name__}")
@@ -94,9 +94,7 @@ def deserialize_channel_update_event(
     ) -> channel_events.ChannelUpdateEvent:
         channel = self._app.entity_factory.deserialize_channel(payload)
         if isinstance(channel, channel_models.GuildChannel):
-            return channel_events.GuildChannelUpdateEvent(
-                app=self._app, shard=shard, channel=channel, old_channel=old_channel
-            )
+            return channel_events.GuildChannelUpdateEvent(shard=shard, channel=channel, old_channel=old_channel)
         if isinstance(channel, channel_models.PrivateChannel):
             raise NotImplementedError("DM channel update events are undocumented behaviour")
         raise TypeError(f"Expected GuildChannel or PrivateChannel but received {type(channel).__name__}")
@@ -106,7 +104,7 @@ def deserialize_channel_delete_event(
     ) -> channel_events.ChannelDeleteEvent:
         channel = self._app.entity_factory.deserialize_channel(payload)
         if isinstance(channel, channel_models.GuildChannel):
-            return channel_events.GuildChannelDeleteEvent(app=self._app, shard=shard, channel=channel)
+            return channel_events.GuildChannelDeleteEvent(shard=shard, channel=channel)
         if isinstance(channel, channel_models.PrivateChannel):
             raise NotImplementedError("DM channel delete events are undocumented behaviour")
         raise TypeError(f"Expected GuildChannel or PrivateChannel but received {type(channel).__name__}")
@@ -151,7 +149,7 @@ def deserialize_invite_create_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
     ) -> channel_events.InviteCreateEvent:
         invite = self._app.entity_factory.deserialize_invite_with_metadata(payload)
-        return channel_events.InviteCreateEvent(app=self._app, shard=shard, invite=invite)
+        return channel_events.InviteCreateEvent(shard=shard, invite=invite)
 
     def deserialize_invite_delete_event(
         self,
@@ -184,12 +182,11 @@ def deserialize_typing_start_event(
             guild_id = snowflakes.Snowflake(payload["guild_id"])
             member = self._app.entity_factory.deserialize_member(payload["member"], guild_id=guild_id)
             return typing_events.GuildTypingEvent(
-                app=self._app,
                 shard=shard,
                 channel_id=channel_id,
                 guild_id=guild_id,
                 timestamp=timestamp,
-                user=member,
+                member=member,
             )
 
         user_id = snowflakes.Snowflake(payload["user_id"])
@@ -210,7 +207,6 @@ def deserialize_guild_create_event(
         assert guild_information.presences is not None
         assert guild_information.voice_states is not None
         return guild_events.GuildAvailableEvent(
-            app=self._app,
             shard=shard,
             guild=guild_information.guild,
             emojis=guild_information.emojis,
@@ -230,7 +226,6 @@ def deserialize_guild_update_event(
     ) -> guild_events.GuildUpdateEvent:
         guild_information = self._app.entity_factory.deserialize_gateway_guild(payload)
         return guild_events.GuildUpdateEvent(
-            app=self._app,
             shard=shard,
             guild=guild_information.guild,
             emojis=guild_information.emojis,
@@ -254,7 +249,6 @@ def deserialize_guild_ban_add_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
     ) -> guild_events.BanCreateEvent:
         return guild_events.BanCreateEvent(
-            app=self._app,
             shard=shard,
             guild_id=snowflakes.Snowflake(payload["guild_id"]),
             user=self._app.entity_factory.deserialize_user(payload["user"]),
@@ -264,7 +258,6 @@ def deserialize_guild_ban_remove_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
     ) -> guild_events.BanDeleteEvent:
         return guild_events.BanDeleteEvent(
-            app=self._app,
             shard=shard,
             guild_id=snowflakes.Snowflake(payload["guild_id"]),
             user=self._app.entity_factory.deserialize_user(payload["user"]),
@@ -345,9 +338,7 @@ def deserialize_presence_update_event(
                 is_system=user_payload.get("system", undefined.UNDEFINED),
                 flags=flags,
             )
-        return guild_events.PresenceUpdateEvent(
-            app=self._app, shard=shard, presence=presence, user=user, old_presence=old_presence
-        )
+        return guild_events.PresenceUpdateEvent(shard=shard, presence=presence, user=user, old_presence=old_presence)
 
     ######################
     # INTERACTION EVENTS #
@@ -398,7 +389,7 @@ def deserialize_guild_member_add_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
     ) -> member_events.MemberCreateEvent:
         member = self._app.entity_factory.deserialize_member(payload)
-        return member_events.MemberCreateEvent(app=self._app, shard=shard, member=member)
+        return member_events.MemberCreateEvent(shard=shard, member=member)
 
     def deserialize_guild_member_update_event(
         self,
@@ -408,7 +399,7 @@ def deserialize_guild_member_update_event(
         old_member: typing.Optional[guild_models.Member],
     ) -> member_events.MemberUpdateEvent:
         member = self._app.entity_factory.deserialize_member(payload)
-        return member_events.MemberUpdateEvent(app=self._app, shard=shard, member=member, old_member=old_member)
+        return member_events.MemberUpdateEvent(shard=shard, member=member, old_member=old_member)
 
     def deserialize_guild_member_remove_event(
         self,
@@ -419,9 +410,7 @@ def deserialize_guild_member_remove_event(
     ) -> member_events.MemberDeleteEvent:
         guild_id = snowflakes.Snowflake(payload["guild_id"])
         user = self._app.entity_factory.deserialize_user(payload["user"])
-        return member_events.MemberDeleteEvent(
-            app=self._app, shard=shard, guild_id=guild_id, user=user, old_member=old_member
-        )
+        return member_events.MemberDeleteEvent(shard=shard, guild_id=guild_id, user=user, old_member=old_member)
 
     ###############
     # ROLE EVENTS #
@@ -434,7 +423,7 @@ def deserialize_guild_role_create_event(
             payload["role"],
             guild_id=snowflakes.Snowflake(payload["guild_id"]),
         )
-        return role_events.RoleCreateEvent(app=self._app, shard=shard, role=role)
+        return role_events.RoleCreateEvent(shard=shard, role=role)
 
     def deserialize_guild_role_update_event(
         self,
@@ -447,7 +436,7 @@ def deserialize_guild_role_update_event(
             payload["role"],
             guild_id=snowflakes.Snowflake(payload["guild_id"]),
         )
-        return role_events.RoleUpdateEvent(app=self._app, shard=shard, role=role, old_role=old_role)
+        return role_events.RoleUpdateEvent(shard=shard, role=role, old_role=old_role)
 
     def deserialize_guild_role_delete_event(
         self,
@@ -490,9 +479,9 @@ def deserialize_message_create_event(
         message = self._app.entity_factory.deserialize_message(payload)
 
         if message.guild_id is None:
-            return message_events.DMMessageCreateEvent(app=self._app, shard=shard, message=message)
+            return message_events.DMMessageCreateEvent(shard=shard, message=message)
 
-        return message_events.GuildMessageCreateEvent(app=self._app, shard=shard, message=message)
+        return message_events.GuildMessageCreateEvent(shard=shard, message=message)
 
     def deserialize_message_update_event(  # noqa: CFQ001
         self,
@@ -504,13 +493,9 @@ def deserialize_message_update_event(  # noqa: CFQ001
         message = self._app.entity_factory.deserialize_partial_message(payload)
 
         if message.guild_id is None:
-            return message_events.DMMessageUpdateEvent(
-                app=self._app, shard=shard, message=message, old_message=old_message
-            )
+            return message_events.DMMessageUpdateEvent(shard=shard, message=message, old_message=old_message)
 
-        return message_events.GuildMessageUpdateEvent(
-            app=self._app, shard=shard, message=message, old_message=old_message
-        )
+        return message_events.GuildMessageUpdateEvent(shard=shard, message=message, old_message=old_message)
 
     def deserialize_message_delete_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
@@ -572,7 +557,6 @@ def deserialize_message_reaction_add_event(
             guild_id = snowflakes.Snowflake(payload["guild_id"])
             member = self._app.entity_factory.deserialize_member(payload["member"], guild_id=guild_id)
             return reaction_events.GuildReactionAddEvent(
-                app=self._app,
                 shard=shard,
                 member=member,
                 channel_id=channel_id,
@@ -684,7 +668,6 @@ def deserialize_ready_event(
         session_id = payload["session_id"]
 
         return shard_events.ShardReadyEvent(
-            app=self._app,
             shard=shard,
             actual_gateway_version=gateway_version,
             session_id=session_id,
@@ -750,7 +733,7 @@ def deserialize_own_user_update_event(
         old_user: typing.Optional[user_models.OwnUser],
     ) -> user_events.OwnUserUpdateEvent:
         return user_events.OwnUserUpdateEvent(
-            app=self._app, shard=shard, user=self._app.entity_factory.deserialize_my_user(payload), old_user=old_user
+            shard=shard, user=self._app.entity_factory.deserialize_my_user(payload), old_user=old_user
         )
 
     ################
@@ -765,7 +748,7 @@ def deserialize_voice_state_update_event(
         old_state: typing.Optional[voices_models.VoiceState],
     ) -> voice_events.VoiceStateUpdateEvent:
         state = self._app.entity_factory.deserialize_voice_state(payload)
-        return voice_events.VoiceStateUpdateEvent(app=self._app, shard=shard, state=state, old_state=old_state)
+        return voice_events.VoiceStateUpdateEvent(shard=shard, state=state, old_state=old_state)
 
     def deserialize_voice_server_update_event(
         self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject
diff --git a/hikari/messages.py b/hikari/messages.py
index 66eff381..fbc51fdc 100644
--- a/hikari/messages.py
+++ b/hikari/messages.py
@@ -331,8 +331,7 @@ def user_ids(self) -> undefined.UndefinedOr[typing.Sequence[snowflakes.Snowflake
 
         return list(self.users.keys())
 
-    @property
-    def members(self) -> undefined.UndefinedOr[typing.Mapping[snowflakes.Snowflake, guilds.Member]]:
+    def get_members(self) -> undefined.UndefinedOr[typing.Mapping[snowflakes.Snowflake, guilds.Member]]:
         """Discover any cached members notified by this message.
 
         If this message was sent in a DM, this will always be empty.
@@ -366,8 +365,7 @@ def members(self) -> undefined.UndefinedOr[typing.Mapping[snowflakes.Snowflake,
 
         return {}
 
-    @property
-    def roles(self) -> typing.Mapping[snowflakes.Snowflake, guilds.Role]:
+    def get_roles(self) -> undefined.UndefinedOr[typing.Mapping[snowflakes.Snowflake, guilds.Role]]:
         """Attempt to look up the roles that are notified by this message.
 
         If this message was sent in a DM, this will always be empty.
@@ -389,10 +387,13 @@ def roles(self) -> typing.Mapping[snowflakes.Snowflake, guilds.Role]:
             in `notifies_role_ids` may not be present here. This is a limitation
             of Discord, again.
         """
+        if self.role_ids is undefined.UNDEFINED:
+            return undefined.UNDEFINED
+
         if isinstance(self._message.app, traits.CacheAware) and self._message.guild_id is not None:
             app = self._message.app
             return self._map_cache_maybe_discover(
-                self.roles,
+                self.role_ids,
                 app.cache.get_role,
             )
 
@@ -659,7 +660,7 @@ class PartialMessage(snowflakes.Unique):
         This will only be provided for interaction messages.
     """
 
-    @property
+    @property  # TODO: update this while refactoring message structure
     def guild_id(self) -> typing.Optional[snowflakes.Snowflake]:
         """ID of the guild that the message was sent in.
 
@@ -699,6 +700,7 @@ def make_link(self, guild: typing.Optional[snowflakes.SnowflakeishOr[guilds.Part
         builtins.str
             The jump link to the message.
         """
+        # TODO: this doesn't seem like a safe assumption for rest only applications
         guild_id_str = "@me" if guild is None else str(int(guild))
         return f"{urls.BASE_URL}/channels/{guild_id_str}/{self.channel_id}/{self.id}"
 
diff --git a/hikari/snowflakes.py b/hikari/snowflakes.py
index e26bc928..05092a0e 100644
--- a/hikari/snowflakes.py
+++ b/hikari/snowflakes.py
@@ -89,16 +89,22 @@ def from_datetime(cls, timestamp: datetime.datetime) -> Snowflake:
     @classmethod
     def min(cls) -> Snowflake:
         """Minimum value for a snowflakes."""
-        if not hasattr(cls, "___MIN___"):
+        try:
+            return cls.___MIN___
+
+        except AttributeError:
             cls.___MIN___ = Snowflake(0)
-        return cls.___MIN___
+            return cls.___MIN___
 
     @classmethod
     def max(cls) -> Snowflake:
         """Maximum value for a snowflakes."""
-        if not hasattr(cls, "___MAX___"):
+        try:
+            return cls.___MAX___
+
+        except AttributeError:
             cls.___MAX___ = Snowflake((1 << 63) - 1)
-        return cls.___MAX___
+            return cls.___MAX___
 
     @classmethod
     def from_data(cls, timestamp: datetime.datetime, worker_id: int, process_id: int, increment: int) -> Snowflake:
diff --git a/hikari/traits.py b/hikari/traits.py
index f108cdd9..eba564a6 100644
--- a/hikari/traits.py
+++ b/hikari/traits.py
@@ -295,22 +295,6 @@ def heartbeat_latency(self) -> float:
         """
         raise NotImplementedError
 
-    @property
-    def me(self) -> typing.Optional[users_.OwnUser]:
-        """Return the bot user, if known.
-
-        This should be available as soon as the bot has fired the
-        `hikari.events.lifetime_events.StartingEvent`.
-
-        Until then, this may or may not be `builtins.None`.
-
-        Returns
-        -------
-        typing.Optional[hikari.users.OwnUser]
-            The bot user, if known, otherwise `builtins.None`.
-        """
-        raise NotImplementedError
-
     @property
     def shards(self) -> typing.Mapping[int, gateway_shard.GatewayShard]:
         """Return a mapping of shards in this application instance.
@@ -341,6 +325,21 @@ def shard_count(self) -> int:
         """
         raise NotImplementedError
 
+    def get_me(self) -> typing.Optional[users_.OwnUser]:
+        """Return the bot user, if known.
+
+        This should be available as soon as the bot has fired the
+        `hikari.events.lifetime_events.StartingEvent`.
+
+        Until then, this may or may not be `builtins.None`.
+
+        Returns
+        -------
+        typing.Optional[hikari.users.OwnUser]
+            The bot user, if known, otherwise `builtins.None`.
+        """
+        raise NotImplementedError
+
     async def update_presence(
         self,
         *,


Test patch:
diff --git a/tests/hikari/events/test_channel_events.py b/tests/hikari/events/test_channel_events.py
index 4981c388..0c2773bf 100644
--- a/tests/hikari/events/test_channel_events.py
+++ b/tests/hikari/events/test_channel_events.py
@@ -39,21 +39,26 @@ def event(self):
         )
         return cls()
 
-    def test_guild_when_available(self, event):
-        result = event.guild
+    def test_get_guild_when_available(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_available_guild.return_value
         event.app.cache.get_available_guild.assert_called_once_with(929292929)
         event.app.cache.get_unavailable_guild.assert_not_called()
 
-    def test_guild_when_unavailable(self, event):
+    def test_get_guild_when_unavailable(self, event):
         event.app.cache.get_available_guild.return_value = None
-        result = event.guild
+        result = event.get_guild()
 
         assert result is event.app.cache.get_unavailable_guild.return_value
         event.app.cache.get_available_guild.assert_called_once_with(929292929)
         event.app.cache.get_unavailable_guild.assert_called_once_with(929292929)
 
+    def test_get_guild_without_cache(self):
+        event = hikari_test_helpers.mock_class_namespace(channel_events.GuildChannelEvent, app=None)()
+
+        assert event.get_guild() is None
+
     @pytest.mark.asyncio()
     async def test_fetch_guild(self, event):
         event.app.rest.fetch_guild = mock.AsyncMock()
@@ -62,12 +67,17 @@ async def test_fetch_guild(self, event):
         assert result is event.app.rest.fetch_guild.return_value
         event.app.rest.fetch_guild.assert_awaited_once_with(929292929)
 
-    def test_channel(self, event):
-        result = event.channel
+    def test_get_channel(self, event):
+        result = event.get_channel()
 
         assert result is event.app.cache.get_guild_channel.return_value
         event.app.cache.get_guild_channel.assert_called_once_with(432432432)
 
+    def test_get_channel_without_cache(self):
+        event = hikari_test_helpers.mock_class_namespace(channel_events.GuildChannelEvent, app=None)()
+
+        assert event.get_channel() is None
+
     @pytest.mark.asyncio()
     async def test_fetch_channel(self, event):
         event.app.rest.fetch_channel = mock.AsyncMock(return_value=mock.MagicMock(spec=channels.GuildChannel))
@@ -83,6 +93,9 @@ def event(self):
         cls = hikari_test_helpers.mock_class_namespace(channel_events.ChannelCreateEvent)
         return cls()
 
+    def test_app_property(self, event):
+        assert event.app is event.channel.app
+
     def test_channel_id_property(self, event):
         event.channel.id = 123
         assert event.channel_id == 123
@@ -91,7 +104,7 @@ def test_channel_id_property(self, event):
 class TestGuildChannelCreateEvent:
     @pytest.fixture()
     def event(self):
-        return channel_events.GuildChannelCreateEvent(app=None, channel=mock.Mock(), shard=None)
+        return channel_events.GuildChannelCreateEvent(channel=mock.Mock(), shard=None)
 
     def test_guild_id_property(self, event):
         event.channel.guild_id = 123
@@ -103,6 +116,9 @@ class TestChannelUpdateEvent:
     def event(self):
         return hikari_test_helpers.mock_class_namespace(channel_events.ChannelUpdateEvent)()
 
+    def test_app_property(self, event):
+        assert event.app is event.channel.app
+
     def test_channel_id_property(self, event):
         event.channel.id = 123
         assert event.channel_id == 123
@@ -111,9 +127,7 @@ def test_channel_id_property(self, event):
 class TestGuildChannelUpdateEvent:
     @pytest.fixture()
     def event(self):
-        return channel_events.GuildChannelUpdateEvent(
-            app=None, channel=mock.Mock(), old_channel=mock.Mock(), shard=None
-        )
+        return channel_events.GuildChannelUpdateEvent(channel=mock.Mock(), old_channel=mock.Mock(), shard=None)
 
     def test_guild_id_property(self, event):
         event.channel.guild_id = 123
@@ -129,6 +143,9 @@ class TestChannelDeleteEvent:
     def event(self):
         return hikari_test_helpers.mock_class_namespace(channel_events.ChannelDeleteEvent)()
 
+    def test_app_property(self, event):
+        assert event.app is event.channel.app
+
     def test_channel_id_property(self, event):
         event.channel.id = 123
         assert event.channel_id == 123
@@ -137,13 +154,30 @@ def test_channel_id_property(self, event):
 class TestGuildChannelDeleteEvent:
     @pytest.fixture()
     def event(self):
-        return channel_events.GuildChannelDeleteEvent(app=None, channel=mock.Mock(), shard=None)
+        return channel_events.GuildChannelDeleteEvent(channel=mock.Mock(), shard=None)
 
     def test_guild_id_property(self, event):
         event.channel.guild_id = 123
         assert event.guild_id == 123
 
 
+class TestGuildPinsUpdateEvent:
+    @pytest.fixture()
+    def event(self):
+        return channel_events.GuildPinsUpdateEvent(
+            app=mock.Mock(), shard=None, channel_id=12343, guild_id=None, last_pin_timestamp=None
+        )
+
+    @pytest.mark.parametrize("result", [mock.Mock(spec=channels.GuildTextChannel), None])
+    def test_get_channel(self, event, result):
+        event.app.cache.get_guild_channel.return_value = result
+
+        result = event.get_channel()
+
+        assert result is event.app.cache.get_guild_channel.return_value
+        event.app.cache.get_guild_channel.assert_called_once_with(event.channel_id)
+
+
 @pytest.mark.asyncio()
 class TestInviteEvent:
     @pytest.fixture()
@@ -164,7 +198,10 @@ async def test_fetch_invite(self, event):
 class TestInviteCreateEvent:
     @pytest.fixture()
     def event(self):
-        return channel_events.InviteCreateEvent(app=None, shard=None, invite=mock.Mock)
+        return channel_events.InviteCreateEvent(shard=None, invite=mock.Mock())
+
+    def test_app_property(self, event):
+        assert event.app is event.invite.app
 
     async def test_channel_id_property(self, event):
         event.invite.channel_id = 123
diff --git a/tests/hikari/events/test_guild_events.py b/tests/hikari/events/test_guild_events.py
index 695e4a06..9d00b7d3 100644
--- a/tests/hikari/events/test_guild_events.py
+++ b/tests/hikari/events/test_guild_events.py
@@ -30,7 +30,7 @@
 from tests.hikari import hikari_test_helpers
 
 
-class GuildEvent:
+class TestGuildEvent:
     @pytest.fixture()
     def event(self):
         cls = hikari_test_helpers.mock_class_namespace(
@@ -38,21 +38,26 @@ def event(self):
         )
         return cls()
 
-    def test_guild_when_available(self, event):
-        result = event.guild
+    def test_get_guild_when_available(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_available_guild.return_value
         event.app.cache.get_available_guild.assert_called_once_with(534123123)
         event.app.cache.get_unavailable_guild.assert_not_called()
 
-    def test_guild_when_unavailable(self, event):
+    def test_get_guild_when_unavailable(self, event):
         event.app.cache.get_available_guild.return_value = None
-        result = event.guild
+        result = event.get_guild()
 
         assert result is event.app.cache.get_unavailable_guild.return_value
         event.app.cache.get_unavailable_guild.assert_called_once_with(534123123)
         event.app.cache.get_available_guild.assert_called_once_with(534123123)
 
+    def test_get_guild_cacheless(self, event):
+        event = hikari_test_helpers.mock_class_namespace(guild_events.GuildEvent, app=object())()
+
+        assert event.get_guild() is None
+
     @pytest.mark.asyncio()
     async def test_fetch_guild(self, event):
         event.app.rest.fetch_guild = mock.AsyncMock()
@@ -74,7 +79,6 @@ class TestGuildAvailableEvent:
     @pytest.fixture()
     def event(self):
         return guild_events.GuildAvailableEvent(
-            app=None,
             shard=object(),
             guild=mock.Mock(guilds.Guild),
             emojis={},
@@ -85,6 +89,9 @@ def event(self):
             voice_states={},
         )
 
+    def test_app_property(self, event):
+        assert event.app is event.guild.app
+
     def test_guild_id_property(self, event):
         event.guild.id = 123
         assert event.guild_id == 123
@@ -94,7 +101,6 @@ class TestGuildUpdateEvent:
     @pytest.fixture()
     def event(self):
         return guild_events.GuildUpdateEvent(
-            app=None,
             shard=object(),
             guild=mock.Mock(guilds.Guild),
             old_guild=mock.Mock(guilds.Guild),
@@ -102,6 +108,9 @@ def event(self):
             roles={},
         )
 
+    def test_app_property(self, event):
+        assert event.app is event.guild.app
+
     def test_guild_id_property(self, event):
         event.guild.id = 123
         assert event.guild_id == 123
@@ -111,17 +120,28 @@ def test_old_guild_id_property(self, event):
         assert event.old_guild.id == 123
 
 
+class TestBanEvent:
+    @pytest.fixture()
+    def event(self):
+        return hikari_test_helpers.mock_class_namespace(guild_events.BanEvent)()
+
+    def test_app_property(self, event):
+        assert event.app is event.user.app
+
+
 class TestPresenceUpdateEvent:
     @pytest.fixture()
     def event(self):
         return guild_events.PresenceUpdateEvent(
-            app=None,
             shard=object(),
             presence=mock.Mock(presences.MemberPresence),
             old_presence=mock.Mock(presences.MemberPresence),
             user=mock.Mock(),
         )
 
+    def test_app_property(self, event):
+        assert event.app is event.presence.app
+
     def test_user_id_property(self, event):
         event.presence.user_id = 123
         assert event.user_id == 123
diff --git a/tests/hikari/events/test_member_events.py b/tests/hikari/events/test_member_events.py
index cfadc467..8cae8278 100644
--- a/tests/hikari/events/test_member_events.py
+++ b/tests/hikari/events/test_member_events.py
@@ -40,16 +40,22 @@ def event(self):
         )
         return cls()
 
+    def test_app_property(self, event):
+        assert event.app is event.user.app
+
     def test_user_id_property(self, event):
         event.user_id == 456
 
-    def test_guild_when_no_cache_trait(self, event):
-        event.app = object()
+    def test_guild_when_no_cache_trait(self):
+        event = hikari_test_helpers.mock_class_namespace(
+            member_events.MemberEvent,
+            app=None,
+        )()
 
-        assert event.guild is None
+        assert event.get_guild() is None
 
-    def test_guild_when_available(self, event):
-        result = event.guild
+    def test_get_guild_when_available(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_available_guild.return_value
         event.app.cache.get_available_guild.assert_called_once_with(123)
@@ -57,7 +63,7 @@ def test_guild_when_available(self, event):
 
     def test_guild_when_unavailable(self, event):
         event.app.cache.get_available_guild.return_value = None
-        result = event.guild
+        result = event.get_guild()
 
         assert result is event.app.cache.get_unavailable_guild.return_value
         event.app.cache.get_unavailable_guild.assert_called_once_with(123)
@@ -67,7 +73,7 @@ def test_guild_when_unavailable(self, event):
 class TestMemberCreateEvent:
     @pytest.fixture()
     def event(self):
-        return member_events.MemberCreateEvent(app=None, shard=None, member=mock.Mock())
+        return member_events.MemberCreateEvent(shard=None, member=mock.Mock())
 
     def test_guild_property(self, event):
         event.member.guild_id = 123
@@ -82,9 +88,7 @@ def test_user_property(self, event):
 class TestMemberUpdateEvent:
     @pytest.fixture()
     def event(self):
-        return member_events.MemberUpdateEvent(
-            app=None, shard=None, member=mock.Mock(), old_member=mock.Mock(guilds.Member)
-        )
+        return member_events.MemberUpdateEvent(shard=None, member=mock.Mock(), old_member=mock.Mock(guilds.Member))
 
     def test_guild_property(self, event):
         event.member.guild_id = 123
diff --git a/tests/hikari/events/test_message_events.py b/tests/hikari/events/test_message_events.py
index 7b6af2ba..138a031e 100644
--- a/tests/hikari/events/test_message_events.py
+++ b/tests/hikari/events/test_message_events.py
@@ -37,7 +37,6 @@ class TestMessageCreateEvent:
     def event(self):
         cls = hikari_test_helpers.mock_class_namespace(
             message_events.MessageCreateEvent,
-            app=object(),
             message=mock.Mock(
                 spec_set=messages.Message,
                 author=mock.Mock(
@@ -49,6 +48,9 @@ def event(self):
 
         return cls()
 
+    def test_app_property(self, event):
+        assert event.app is event.message.app
+
     def test_author_property(self, event):
         assert event.author is event.message.author
 
@@ -97,7 +99,6 @@ class TestMessageUpdateEvent:
     def event(self):
         cls = hikari_test_helpers.mock_class_namespace(
             message_events.MessageUpdateEvent,
-            app=object(),
             message=mock.Mock(
                 spec_set=messages.Message,
                 author=mock.Mock(
@@ -109,6 +110,9 @@ def event(self):
 
         return cls()
 
+    def test_app_property(self, event):
+        assert event.app is event.message.app
+
     @pytest.mark.parametrize("author", [mock.Mock(spec_set=users.User), undefined.UNDEFINED])
     def test_author_property(self, event, author):
         event.message.author = author
@@ -169,7 +173,6 @@ class TestGuildMessageCreateEvent:
     @pytest.fixture()
     def event(self):
         return message_events.GuildMessageCreateEvent(
-            app=mock.Mock(),
             message=mock.Mock(
                 spec_set=messages.Message,
                 guild_id=snowflakes.Snowflake(342123123),
@@ -181,26 +184,30 @@ def event(self):
     def test_guild_id_property(self, event):
         assert event.guild_id == snowflakes.Snowflake(342123123)
 
-    def test_channel_property_when_no_cache_trait(self, event):
-        event.app = object()
+    def test_get_channel_when_no_cache_trait(self):
+        event = hikari_test_helpers.mock_class_namespace(
+            message_events.GuildMessageCreateEvent, app=None, init_=False
+        )()
 
-        assert event.channel is None
+        assert event.get_channel() is None
 
     @pytest.mark.parametrize("guild_channel_impl", [channels.GuildTextChannel, channels.GuildNewsChannel])
-    def test_channel_property(self, event, guild_channel_impl):
+    def test_get_channel(self, event, guild_channel_impl):
         event.app.cache.get_guild_channel = mock.Mock(return_value=mock.Mock(spec_set=guild_channel_impl))
 
-        result = event.channel
+        result = event.get_channel()
         assert result is event.app.cache.get_guild_channel.return_value
         event.app.cache.get_guild_channel.assert_called_once_with(9121234)
 
-    def test_guild_property_when_no_cache_trait(self, event):
-        event.app = object()
+    def test_get_guild_when_no_cache_trait(self):
+        event = hikari_test_helpers.mock_class_namespace(
+            message_events.GuildMessageCreateEvent, app=None, init_=False
+        )()
 
-        assert event.guild is None
+        assert event.get_guild() is None
 
-    def test_guild_property(self, event):
-        result = event.guild
+    def test_get_guild(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_guild.return_value
         event.app.cache.get_guild.assert_called_once_with(342123123)
@@ -211,12 +218,24 @@ def test_author_property(self, event):
     def test_member_property(self, event):
         assert event.member is event.message.member
 
+    def test_get_member_when_cacheless(self, event):
+        event.message.app = None
+
+        result = event.get_member()
+
+        assert result is None
+
+    def test_get_member(self, event):
+        result = event.get_member()
+
+        assert result is event.app.cache.get_member.return_value
+        event.app.cache.get_member.assert_called_once_with(event.guild_id, event.author_id)
+
 
 class TestGuildMessageUpdateEvent:
     @pytest.fixture()
     def event(self):
         return message_events.GuildMessageUpdateEvent(
-            app=mock.Mock(),
             message=mock.Mock(
                 spec_set=messages.Message,
                 guild_id=snowflakes.Snowflake(54123123123),
@@ -232,64 +251,46 @@ def test_author_property(self, event):
     def test_member_property(self, event):
         assert event.member is event.message.member
 
-    def test_member_property_when_member_defined(self, event):
-        event.message.member = mock.Mock()
-        event.message.author = undefined.UNDEFINED
-
-        assert event.member is event.message.member
-
-    def test_member_property_when_member_none_but_cached(self, event):
-        event.message.member = None
-        event.message.author = mock.Mock(id=1234321)
-        event.message.guild_id = snowflakes.Snowflake(696969)
-        real_member = mock.Mock()
-        event.app.cache.get_member = mock.Mock(return_value=real_member)
-
-        assert event.member is real_member
-
-        event.app.cache.get_member.assert_called_once_with(696969, 1234321)
-
-    def test_member_property_when_member_none_and_author_none(self, event):
-        event.message.author = None
-        event.message.member = None
-
-        assert event.member is None
-
-        event.app.cache.get_member.assert_not_called()
-
-    def test_member_property_when_member_none_and_uncached(self, event):
-        event.message.member = None
-        event.message.author = mock.Mock(id=1234321)
-        event.message.guild_id = snowflakes.Snowflake(696969)
-        event.app.cache.get_member = mock.Mock(return_value=None)
-
-        assert event.member is None
-
-        event.app.cache.get_member.assert_called_once_with(696969, 1234321)
-
     def test_guild_id_property(self, event):
         assert event.guild_id == snowflakes.Snowflake(54123123123)
 
-    def test_channel_property_when_no_cache_trait(self, event):
-        event.app = object()
+    def test_get_channel_when_no_cache_trait(self):
+        event = hikari_test_helpers.mock_class_namespace(
+            message_events.GuildMessageUpdateEvent, app=None, init_=False
+        )()
 
-        assert event.channel is None
+        assert event.get_channel() is None
 
     @pytest.mark.parametrize("guild_channel_impl", [channels.GuildTextChannel, channels.GuildNewsChannel])
-    def test_channel_property(self, event, guild_channel_impl):
+    def test_get_channel(self, event, guild_channel_impl):
         event.app.cache.get_guild_channel = mock.Mock(return_value=mock.Mock(spec_set=guild_channel_impl))
 
-        result = event.channel
+        result = event.get_channel()
         assert result is event.app.cache.get_guild_channel.return_value
         event.app.cache.get_guild_channel.assert_called_once_with(800001066)
 
-    def test_guild_property_when_no_cache_trait(self, event):
-        event.app = object()
+    def test_get_member_when_cacheless(self, event):
+        event.message.app = None
+
+        result = event.get_member()
+
+        assert result is None
 
-        assert event.guild is None
+    def test_get_member(self, event):
+        result = event.get_member()
 
-    def test_guild_property(self, event):
-        result = event.guild
+        assert result is event.app.cache.get_member.return_value
+        event.app.cache.get_member.assert_called_once_with(event.guild_id, event.author_id)
+
+    def test_get_guild_when_no_cache_trait(self):
+        event = hikari_test_helpers.mock_class_namespace(
+            message_events.GuildMessageUpdateEvent, app=None, init_=False
+        )()
+
+        assert event.get_guild() is None
+
+    def test_get_guild(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_guild.return_value
         event.app.cache.get_guild.assert_called_once_with(54123123123)
@@ -302,7 +303,6 @@ class TestDMMessageUpdateEvent:
     @pytest.fixture()
     def event(self):
         return message_events.DMMessageUpdateEvent(
-            app=mock.Mock(),
             message=mock.Mock(
                 spec_set=messages.Message, author=mock.Mock(spec_set=users.User, id=snowflakes.Snowflake(8000010662))
             ),
@@ -337,26 +337,26 @@ def event(self):
             is_bulk=True,
         )
 
-    def test_channel_property_when_no_cache_trait(self, event):
+    def test_get_channel_when_no_cache_trait(self, event):
         event.app = object()
 
-        assert event.channel is None
+        assert event.get_channel() is None
 
     @pytest.mark.parametrize("guild_channel_impl", [channels.GuildTextChannel, channels.GuildNewsChannel])
-    def test_channel_property(self, event, guild_channel_impl):
+    def test_get_channel(self, event, guild_channel_impl):
         event.app.cache.get_guild_channel = mock.Mock(return_value=mock.Mock(spec_set=guild_channel_impl))
-        result = event.channel
+        result = event.get_channel()
 
         assert result is event.app.cache.get_guild_channel.return_value
         event.app.cache.get_guild_channel.assert_called_once_with(54213123123)
 
-    def test_guild_property_when_no_cache_trait(self, event):
+    def test_get_guild_when_no_cache_trait(self, event):
         event.app = object()
 
-        assert event.guild is None
+        assert event.get_guild() is None
 
-    def test_guild_property(self, event):
-        result = event.guild
+    def test_get_guild_property(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_guild.return_value
         event.app.cache.get_guild.assert_called_once_with(542342354564)
diff --git a/tests/hikari/events/test_reaction_events.py b/tests/hikari/events/test_reaction_events.py
index 4601804b..fc8394c0 100644
--- a/tests/hikari/events/test_reaction_events.py
+++ b/tests/hikari/events/test_reaction_events.py
@@ -31,9 +31,12 @@ class TestGuildReactionAddEvent:
     @pytest.fixture()
     def event(self):
         return reaction_events.GuildReactionAddEvent(
-            app=None, shard=object(), member=mock.MagicMock(guilds.Member), channel_id=123, message_id=456, emoji="👌"
+            shard=object(), member=mock.MagicMock(guilds.Member), channel_id=123, message_id=456, emoji="👌"
         )
 
+    def test_app_property(self, event):
+        assert event.app is event.member.app
+
     def test_guild_id_property(self, event):
         event.member.guild_id = 123
         assert event.guild_id == 123
diff --git a/tests/hikari/events/test_role_events.py b/tests/hikari/events/test_role_events.py
index 8f1ec8c1..be12d018 100644
--- a/tests/hikari/events/test_role_events.py
+++ b/tests/hikari/events/test_role_events.py
@@ -30,7 +30,10 @@
 class TestRoleCreateEvent:
     @pytest.fixture()
     def event(self):
-        return role_events.RoleCreateEvent(app=None, shard=object(), role=mock.Mock(guilds.Role))
+        return role_events.RoleCreateEvent(shard=object(), role=mock.Mock(guilds.Role))
+
+    def test_app_property(self, event):
+        assert event.app is event.role.app
 
     def test_guild_id_property(self, event):
         event.role.guild_id = 123
@@ -44,9 +47,10 @@ def test_role_id_property(self, event):
 class TestRoleUpdateEvent:
     @pytest.fixture()
     def event(self):
-        return role_events.RoleUpdateEvent(
-            app=None, shard=object(), role=mock.Mock(guilds.Role), old_role=mock.Mock(guilds.Role)
-        )
+        return role_events.RoleUpdateEvent(shard=object(), role=mock.Mock(guilds.Role), old_role=mock.Mock(guilds.Role))
+
+    def test_app_property(self, event):
+        assert event.app is event.role.app
 
     def test_guild_id_property(self, event):
         event.role.guild_id = 123
diff --git a/tests/hikari/events/test_shard_events.py b/tests/hikari/events/test_shard_events.py
index 769c66a5..474d8b7b 100644
--- a/tests/hikari/events/test_shard_events.py
+++ b/tests/hikari/events/test_shard_events.py
@@ -26,6 +26,23 @@
 from hikari.events import shard_events
 
 
+class TestShardReadyEvent:
+    @pytest.fixture()
+    def event(self):
+        return shard_events.ShardReadyEvent(
+            my_user=mock.Mock(),
+            shard=None,
+            actual_gateway_version=1,
+            session_id="ok",
+            application_id=1,
+            application_flags=1,
+            unavailable_guilds=[],
+        )
+
+    def test_app_property(self, event):
+        assert event.app is event.my_user.app
+
+
 class TestMemberChunkEvent:
     @pytest.fixture()
     def event(self):
diff --git a/tests/hikari/events/test_typing_events.py b/tests/hikari/events/test_typing_events.py
index 6f557d06..ae1509ed 100644
--- a/tests/hikari/events/test_typing_events.py
+++ b/tests/hikari/events/test_typing_events.py
@@ -24,7 +24,6 @@
 import pytest
 
 from hikari import channels
-from hikari import users
 from hikari.events import typing_events
 from tests.hikari import hikari_test_helpers
 
@@ -43,6 +42,14 @@ def event(self):
 
         return cls()
 
+    async def test_get_user_when_no_cache(self, event):
+        event = hikari_test_helpers.mock_class_namespace(typing_events.TypingEvent, app=None)()
+
+        assert event.get_user() is None
+
+    def test_get_user(self, event):
+        assert event.get_user() is event.app.cache.get_user.return_value
+
     async def test_trigger_typing(self, event):
         event.app.rest.trigger_typing = mock.Mock()
         result = event.trigger_typing()
@@ -60,46 +67,48 @@ def event(self):
             channel_id=123,
             timestamp=object(),
             shard=object(),
-            app=mock.Mock(rest=mock.AsyncMock()),
             guild_id=789,
-            user=mock.Mock(id=456),
+            member=mock.Mock(id=456, app=mock.Mock(rest=mock.AsyncMock())),
         )
 
-    def test_channel_when_no_cache(self, event):
-        event.app = object()
+    def test_app_property(self, event):
+        assert event.app is event.member.app
+
+    def test_get_channel_when_no_cache(self):
+        event = hikari_test_helpers.mock_class_namespace(typing_events.GuildTypingEvent, app=None, init_=False)()
 
-        assert event.channel is None
+        assert event.get_channel() is None
 
     @pytest.mark.parametrize("guild_channel_impl", [channels.GuildNewsChannel, channels.GuildTextChannel])
-    def test_channel(self, event, guild_channel_impl):
+    def test_get_channel(self, event, guild_channel_impl):
         event.app.cache.get_guild_channel = mock.Mock(return_value=mock.Mock(spec_set=guild_channel_impl))
-        result = event.channel
+        result = event.get_channel()
 
         assert result is event.app.cache.get_guild_channel.return_value
         event.app.cache.get_guild_channel.assert_called_once_with(123)
 
-    async def test_guild_when_no_cache(self, event):
-        event.app = object()
+    async def test_get_guild_when_no_cache(self):
+        event = hikari_test_helpers.mock_class_namespace(typing_events.GuildTypingEvent, app=None, init_=False)()
 
-        assert event.guild is None
+        assert event.get_guild() is None
 
-    def test_guild_when_available(self, event):
-        result = event.guild
+    def test_get_guild_when_available(self, event):
+        result = event.get_guild()
 
         assert result is event.app.cache.get_available_guild.return_value
         event.app.cache.get_available_guild.assert_called_once_with(789)
         event.app.cache.get_unavailable_guild.assert_not_called()
 
-    def test_guild_when_unavailable(self, event):
+    def test_get_guild_when_unavailable(self, event):
         event.app.cache.get_available_guild.return_value = None
-        result = event.guild
+        result = event.get_guild()
 
         assert result is event.app.cache.get_unavailable_guild.return_value
         event.app.cache.get_unavailable_guild.assert_called_once_with(789)
         event.app.cache.get_available_guild.assert_called_once_with(789)
 
     def test_user_id(self, event):
-        assert event.user_id == event.user.id
+        assert event.user_id == event.member.id
         assert event.user_id == 456
 
     @pytest.mark.parametrize("guild_channel_impl", [channels.GuildNewsChannel, channels.GuildTextChannel])
@@ -119,8 +128,8 @@ async def test_fetch_guild_preview(self, event):
 
         event.app.rest.fetch_guild_preview.assert_awaited_once_with(789)
 
-    async def test_fetch_user(self, event):
-        await event.fetch_user()
+    async def test_fetch_member(self, event):
+        await event.fetch_member()
 
         event.app.rest.fetch_member.assert_awaited_once_with(789, 456)
 
@@ -139,16 +148,6 @@ def event(self):
             user_id=456,
         )
 
-    async def test_user_when_no_cache(self, event):
-        event.app = object()
-
-        assert event.user is None
-
-    def test_user(self, event):
-        event.app.cache.get_user = mock.Mock(return_value=mock.Mock(spec_set=users.User))
-
-        assert event.user is event.app.cache.get_user.return_value
-
     async def test_fetch_channel(self, event):
         event.app.rest.fetch_channel = mock.AsyncMock(return_value=mock.Mock(spec_set=channels.DMChannel))
         await event.fetch_channel()
diff --git a/tests/hikari/events/test_user_events.py b/tests/hikari/events/test_user_events.py
new file mode 100644
index 00000000..ff697a5a
--- /dev/null
+++ b/tests/hikari/events/test_user_events.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020 Nekokatt
+# Copyright (c) 2021 davfsa
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import mock
+import pytest
+
+from hikari.events import user_events
+
+
+class TestOwnUserUpdateEvent:
+    @pytest.fixture()
+    def event(self):
+        return user_events.OwnUserUpdateEvent(shard=None, old_user=None, user=mock.Mock())
+
+    def test_app_property(self, event):
+        assert event.app is event.user.app
diff --git a/tests/hikari/events/test_voice_events.py b/tests/hikari/events/test_voice_events.py
index f8eb3e91..6379e642 100644
--- a/tests/hikari/events/test_voice_events.py
+++ b/tests/hikari/events/test_voice_events.py
@@ -31,9 +31,12 @@ class TestVoiceStateUpdateEvent:
     @pytest.fixture()
     def event(self):
         return voice_events.VoiceStateUpdateEvent(
-            app=None, shard=object(), state=mock.Mock(voices.VoiceState), old_state=mock.Mock(voices.VoiceState)
+            shard=object(), state=mock.Mock(voices.VoiceState), old_state=mock.Mock(voices.VoiceState)
         )
 
+    def test_app_property(self, event):
+        assert event.app is event.state.app
+
     def test_guild_id_property(self, event):
         event.state.guild_id = 123
         assert event.guild_id == 123
diff --git a/tests/hikari/impl/test_bot.py b/tests/hikari/impl/test_bot.py
index 9e81961a..a0609fd9 100644
--- a/tests/hikari/impl/test_bot.py
+++ b/tests/hikari/impl/test_bot.py
@@ -239,8 +239,8 @@ def test_http_settings(self, bot, http_settings):
     def test_intents(self, bot, intents):
         assert bot.intents is intents
 
-    def test_me(self, bot, cache):
-        assert bot.me is cache.get_me.return_value
+    def test_get_me(self, bot, cache):
+        assert bot.get_me() is cache.get_me.return_value
 
     def test_proxy_settings(self, bot, proxy_settings):
         assert bot.proxy_settings is proxy_settings
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index cf0f3ef9..2da36cbb 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -3431,7 +3431,7 @@ def test_deserialize_partial_message(
         assert partial_message.app is mock_app
         assert partial_message.id == 123
         assert partial_message.channel_id == 456
-        assert partial_message._guild_id == 678
+        assert partial_message.guild_id == 678
         assert partial_message.author == entity_factory_impl.deserialize_user(user_payload)
         assert partial_message.member == entity_factory_impl.deserialize_member(
             member_payload, user=partial_message.author, guild_id=snowflakes.Snowflake(678)
@@ -3537,7 +3537,7 @@ def test_deserialize_partial_message_with_partial_fields(self, entity_factory_im
 
         assert partial_message.content is None
         assert partial_message.edited_timestamp is None
-        assert partial_message._guild_id is not None
+        assert partial_message.guild_id is not None
         assert partial_message.member is None
         assert partial_message.application.primary_sku_id is None
         assert partial_message.application.icon_hash is None
@@ -3552,7 +3552,7 @@ def test_deserialize_partial_message_with_unset_fields(self, entity_factory_impl
         assert partial_message.app is mock_app
         assert partial_message.id == 123
         assert partial_message.channel_id == 456
-        assert partial_message._guild_id is None
+        assert partial_message.guild_id is None
         assert partial_message.author is None
         assert partial_message.member is None
         assert partial_message.content is undefined.UNDEFINED
@@ -3605,7 +3605,7 @@ def side_effect(call_args):
         assert message.app is mock_app
         assert message.id == 123
         assert message.channel_id == 456
-        assert message._guild_id == 678
+        assert message.guild_id == 678
         assert message.author == entity_factory_impl.deserialize_user(user_payload)
         assert message.member == entity_factory_impl.deserialize_member(
             member_payload, user=message.author, guild_id=snowflakes.Snowflake(678)
@@ -3719,7 +3719,7 @@ def test_deserialize_message_with_null_and_unset_fields(
         message = entity_factory_impl.deserialize_message(message_payload)
         assert message.app is mock_app
         assert message.content is None
-        assert message._guild_id is None
+        assert message.guild_id is None
         assert message.member is None
         assert message.edited_timestamp is None
         assert message.mentions.everyone is True
diff --git a/tests/hikari/impl/test_event_factory.py b/tests/hikari/impl/test_event_factory.py
index 92202a9b..61293762 100644
--- a/tests/hikari/impl/test_event_factory.py
+++ b/tests/hikari/impl/test_event_factory.py
@@ -62,13 +62,12 @@ def event_factory(self, mock_app):
 
     def test_deserialize_channel_create_event_for_guild_channel(self, event_factory, mock_app, mock_shard):
         mock_app.entity_factory.deserialize_channel.return_value = mock.Mock(spec=channel_models.GuildChannel)
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_channel_create_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_channel.assert_called_once_with(mock_payload)
         assert isinstance(event, channel_events.GuildChannelCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.channel is mock_app.entity_factory.deserialize_channel.return_value
 
@@ -91,7 +90,6 @@ def test_deserialize_channel_update_event_with_guild_channel(self, event_factory
 
         mock_app.entity_factory.deserialize_channel.assert_called_once_with(mock_payload)
         assert isinstance(event, channel_events.GuildChannelUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.channel is mock_app.entity_factory.deserialize_channel.return_value
         assert event.old_channel is mock_old_channel
@@ -108,13 +106,12 @@ def test_deserialize_channel_update_event_with_unexpected_channel_type(self, eve
 
     def test_deserialize_channel_delete_event_with_guild_channel(self, event_factory, mock_app, mock_shard):
         mock_app.entity_factory.deserialize_channel.return_value = mock.Mock(spec=channel_models.GuildChannel)
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_channel_delete_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_channel.assert_called_once_with(mock_payload)
         assert isinstance(event, channel_events.GuildChannelDeleteEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.channel is mock_app.entity_factory.deserialize_channel.return_value
 
@@ -175,13 +172,12 @@ def test_deserialize_webhook_update_event(self, event_factory, mock_app, mock_sh
         assert event.guild_id == 123123
 
     def test_deserialize_invite_create_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_invite_create_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_invite_with_metadata.assert_called_once_with(mock_payload)
         assert isinstance(event, channel_events.InviteCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.invite is mock_app.entity_factory.deserialize_invite_with_metadata.return_value
 
@@ -211,17 +207,17 @@ def test_deserialize_typing_start_event_for_guild(self, event_factory, mock_app,
             "timestamp": 7634521233,
             "member": mock_member_payload,
         }
+        mock_app.entity_factory.deserialize_member.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_typing_start_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_member.assert_called_once_with(mock_member_payload, guild_id=123321)
         assert isinstance(event, typing_events.GuildTypingEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.channel_id == 48585858
         assert event.guild_id == 123321
         assert event.timestamp == datetime.datetime(2211, 12, 6, 12, 20, 33, tzinfo=datetime.timezone.utc)
-        assert event.user == mock_app.entity_factory.deserialize_member.return_value
+        assert event.member == mock_app.entity_factory.deserialize_member.return_value
 
     def test_deserialize_typing_start_event_for_dm(self, event_factory, mock_app, mock_shard):
         mock_payload = {"channel_id": "534123", "timestamp": 7634521212, "user_id": "9494994"}
@@ -240,13 +236,12 @@ def test_deserialize_typing_start_event_for_dm(self, event_factory, mock_app, mo
     ################
 
     def test_deserialize_guild_create_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_guild_create_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_gateway_guild.assert_called_once_with(mock_payload)
         assert isinstance(event, guild_events.GuildAvailableEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild is mock_app.entity_factory.deserialize_gateway_guild.return_value.guild
         assert event.emojis is mock_app.entity_factory.deserialize_gateway_guild.return_value.emojis
@@ -257,14 +252,13 @@ def test_deserialize_guild_create_event(self, event_factory, mock_app, mock_shar
         assert event.voice_states is mock_app.entity_factory.deserialize_gateway_guild.return_value.voice_states
 
     def test_deserialize_guild_update_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_old_guild = object()
 
         event = event_factory.deserialize_guild_update_event(mock_shard, mock_payload, old_guild=mock_old_guild)
 
         mock_app.entity_factory.deserialize_gateway_guild.assert_called_once_with(mock_payload)
         assert isinstance(event, guild_events.GuildUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild is mock_app.entity_factory.deserialize_gateway_guild.return_value.guild
         assert event.emojis is mock_app.entity_factory.deserialize_gateway_guild.return_value.emojis
@@ -292,27 +286,25 @@ def test_deserialize_guild_unavailable_event(self, event_factory, mock_app, mock
         assert event.guild_id == 6541233
 
     def test_deserialize_guild_ban_add_event(self, event_factory, mock_app, mock_shard):
-        mock_user_payload = object()
+        mock_user_payload = mock.Mock(app=mock_app)
         mock_payload = {"guild_id": "4212312", "user": mock_user_payload}
 
         event = event_factory.deserialize_guild_ban_add_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_user.assert_called_once_with(mock_user_payload)
         assert isinstance(event, guild_events.BanCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild_id == 4212312
         assert event.user is mock_app.entity_factory.deserialize_user.return_value
 
     def test_deserialize_guild_ban_remove_event(self, event_factory, mock_app, mock_shard):
-        mock_user_payload = object()
+        mock_user_payload = mock.Mock(app=mock_app)
         mock_payload = {"guild_id": "9292929", "user": mock_user_payload}
 
         event = event_factory.deserialize_guild_ban_remove_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_user.assert_called_once_with(mock_user_payload)
         assert isinstance(event, guild_events.BanDeleteEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild_id == 9292929
         assert event.user is mock_app.entity_factory.deserialize_user.return_value
@@ -380,6 +372,7 @@ def test_deserialize_integration_update_event(self, event_factory, mock_app, moc
     def test_deserialize_presence_update_event_with_only_user_id(self, event_factory, mock_app, mock_shard):
         mock_payload = {"user": {"id": "1231312"}}
         mock_old_presence = object()
+        mock_app.entity_factory.deserialize_member_presence.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_presence_update_event(
             mock_shard, mock_payload, old_presence=mock_old_presence
@@ -387,7 +380,6 @@ def test_deserialize_presence_update_event_with_only_user_id(self, event_factory
 
         mock_app.entity_factory.deserialize_member_presence.assert_called_once_with(mock_payload)
         assert isinstance(event, guild_events.PresenceUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.old_presence is mock_old_presence
         assert event.user is None
@@ -405,7 +397,7 @@ def test_deserialize_presence_update_event_with_full_user_object(self, event_fac
                 "discriminator": "1231",
             }
         }
-        mock_old_presence = object()
+        mock_old_presence = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_presence_update_event(
             mock_shard, mock_payload, old_presence=mock_old_presence
@@ -413,7 +405,6 @@ def test_deserialize_presence_update_event_with_full_user_object(self, event_fac
 
         mock_app.entity_factory.deserialize_member_presence.assert_called_once_with(mock_payload)
         assert isinstance(event, guild_events.PresenceUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.old_presence is mock_old_presence
 
@@ -431,6 +422,7 @@ def test_deserialize_presence_update_event_with_full_user_object(self, event_fac
     def test_deserialize_presence_update_event_with_partial_user_object(self, event_factory, mock_app, mock_shard):
         mock_payload = {"user": {"id": "1231312", "e": "OK"}}
         mock_old_presence = object()
+        mock_app.entity_factory.deserialize_member_presence.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_presence_update_event(
             mock_shard, mock_payload, old_presence=mock_old_presence
@@ -438,7 +430,6 @@ def test_deserialize_presence_update_event_with_partial_user_object(self, event_
 
         mock_app.entity_factory.deserialize_member_presence.assert_called_once_with(mock_payload)
         assert isinstance(event, guild_events.PresenceUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.old_presence is mock_old_presence
 
@@ -502,18 +493,17 @@ def test_deserialize_interaction_create_event(self, event_factory, mock_app, moc
     #################
 
     def test_deserialize_guild_member_add_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_guild_member_add_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_member.assert_called_once_with(mock_payload)
         assert isinstance(event, member_events.MemberCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.member is mock_app.entity_factory.deserialize_member.return_value
 
     def test_deserialize_guild_member_update_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_old_member = object()
 
         event = event_factory.deserialize_guild_member_update_event(
@@ -522,13 +512,12 @@ def test_deserialize_guild_member_update_event(self, event_factory, mock_app, mo
 
         mock_app.entity_factory.deserialize_member.assert_called_once_with(mock_payload)
         assert isinstance(event, member_events.MemberUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.member is mock_app.entity_factory.deserialize_member.return_value
         assert event.old_member is mock_old_member
 
     def test_deserialize_guild_member_remove_event(self, event_factory, mock_app, mock_shard):
-        mock_user_payload = object()
+        mock_user_payload = mock.Mock(app=mock_app)
         mock_old_member = object()
         mock_payload = {"guild_id": "43123", "user": mock_user_payload}
 
@@ -538,7 +527,6 @@ def test_deserialize_guild_member_remove_event(self, event_factory, mock_app, mo
 
         mock_app.entity_factory.deserialize_user.assert_called_once_with(mock_user_payload)
         assert isinstance(event, member_events.MemberDeleteEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.guild_id == 43123
         assert event.user is mock_app.entity_factory.deserialize_user.return_value
@@ -549,19 +537,18 @@ def test_deserialize_guild_member_remove_event(self, event_factory, mock_app, mo
     ###############
 
     def test_deserialize_guild_role_create_event(self, event_factory, mock_app, mock_shard):
-        mock_role_payload = object()
+        mock_role_payload = mock.Mock(app=mock_app)
         mock_payload = {"role": mock_role_payload, "guild_id": "45123"}
 
         event = event_factory.deserialize_guild_role_create_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_role.assert_called_once_with(mock_role_payload, guild_id=45123)
         assert isinstance(event, role_events.RoleCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.role is mock_app.entity_factory.deserialize_role.return_value
 
     def test_deserialize_guild_role_update_event(self, event_factory, mock_app, mock_shard):
-        mock_role_payload = object()
+        mock_role_payload = mock.Mock(app=mock_app)
         mock_old_role = object()
         mock_payload = {"role": mock_role_payload, "guild_id": "45123"}
 
@@ -569,7 +556,6 @@ def test_deserialize_guild_role_update_event(self, event_factory, mock_app, mock
 
         mock_app.entity_factory.deserialize_role.assert_called_once_with(mock_role_payload, guild_id=45123)
         assert isinstance(event, role_events.RoleUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.role is mock_app.entity_factory.deserialize_role.return_value
         assert event.old_role is mock_old_role
@@ -620,49 +606,45 @@ def test_deserialize_stopped_event(self, event_factory, mock_app):
     ##################
 
     def test_deserialize_message_create_event_in_guild(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_app.entity_factory.deserialize_message.return_value = mock.Mock(guild_id=123321)
 
         event = event_factory.deserialize_message_create_event(mock_shard, mock_payload)
 
         assert isinstance(event, message_events.GuildMessageCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.message is mock_app.entity_factory.deserialize_message.return_value
 
     def test_deserialize_message_create_event_in_dm(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_app.entity_factory.deserialize_message.return_value = mock.Mock(guild_id=None)
 
         event = event_factory.deserialize_message_create_event(mock_shard, mock_payload)
 
         assert isinstance(event, message_events.DMMessageCreateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.message is mock_app.entity_factory.deserialize_message.return_value
 
     def test_deserialize_message_update_event_in_guild(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_old_message = object()
-        mock_app.entity_factory.deserialize_partial_message.return_value = mock.Mock(guild_id=123321)
+        mock_app.entity_factory.deserialize_partial_message.return_value = mock.Mock(guild_id=123321, app=mock_app)
 
         event = event_factory.deserialize_message_update_event(mock_shard, mock_payload, old_message=mock_old_message)
 
         assert isinstance(event, message_events.GuildMessageUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.message is mock_app.entity_factory.deserialize_partial_message.return_value
         assert event.old_message is mock_old_message
 
     def test_deserialize_message_update_event_in_dm(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_old_message = object()
         mock_app.entity_factory.deserialize_partial_message.return_value = mock.Mock(guild_id=None)
 
         event = event_factory.deserialize_message_update_event(mock_shard, mock_payload, old_message=mock_old_message)
 
         assert isinstance(event, message_events.DMMessageUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.message is mock_app.entity_factory.deserialize_partial_message.return_value
         assert event.old_message is mock_old_message
@@ -722,7 +704,7 @@ def test_deserialize_message_delete_bulk_event_in_dm(self, event_factory, mock_a
     ###################
 
     def test_deserialize_message_reaction_add_event_in_guild(self, event_factory, mock_shard, mock_app):
-        mock_member_payload = object()
+        mock_member_payload = mock.Mock(app=mock_app)
         mock_emoji_payload = object()
         mock_payload = {
             "member": mock_member_payload,
@@ -737,7 +719,6 @@ def test_deserialize_message_reaction_add_event_in_guild(self, event_factory, mo
         mock_app.entity_factory.deserialize_emoji.assert_called_once_with(mock_emoji_payload)
         mock_app.entity_factory.deserialize_member.assert_called_once_with(mock_member_payload, guild_id=43949494)
         assert isinstance(event, reaction_events.GuildReactionAddEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.channel_id == 34123
         assert event.message_id == 43123123
@@ -881,12 +862,12 @@ def test_deserialize_ready_event(self, event_factory, mock_app, mock_shard):
             "session_id": "kjsdjiodsaiosad",
             "application": {"id": "4123212", "flags": "4949494"},
         }
+        mock_app.entity_factory.deserialize_my_user.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_ready_event(mock_shard, mock_payload)
 
         mock_app.entity_factory.deserialize_my_user.assert_called_once_with(mock_user_payload)
         assert isinstance(event, shard_events.ShardReadyEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.actual_gateway_version == 69
         assert event.my_user is mock_app.entity_factory.deserialize_my_user.return_value
@@ -966,14 +947,14 @@ def test_deserialize_guild_member_chunk_event_without_optional_fields(self, even
     ###############
 
     def test_deserialize_own_user_update_event(self, event_factory, mock_app, mock_shard):
-        mock_payload = object()
+        mock_payload = mock.Mock(app=mock_app)
         mock_old_user = object()
+        mock_app.entity_factory.deserialize_my_user.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_own_user_update_event(mock_shard, mock_payload, old_user=mock_old_user)
 
         mock_app.entity_factory.deserialize_my_user.assert_called_once_with(mock_payload)
         assert isinstance(event, user_events.OwnUserUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.user is mock_app.entity_factory.deserialize_my_user.return_value
         assert event.old_user is mock_old_user
@@ -985,6 +966,7 @@ def test_deserialize_own_user_update_event(self, event_factory, mock_app, mock_s
     def test_deserialize_voice_state_update_event(self, event_factory, mock_app, mock_shard):
         mock_payload = object()
         mock_old_voice_state = object()
+        mock_app.entity_factory.deserialize_voice_state.return_value = mock.Mock(app=mock_app)
 
         event = event_factory.deserialize_voice_state_update_event(
             mock_shard, mock_payload, old_state=mock_old_voice_state
@@ -992,7 +974,6 @@ def test_deserialize_voice_state_update_event(self, event_factory, mock_app, moc
 
         mock_app.entity_factory.deserialize_voice_state.assert_called_once_with(mock_payload)
         assert isinstance(event, voice_events.VoiceStateUpdateEvent)
-        assert event.app is mock_app
         assert event.shard is mock_shard
         assert event.state is mock_app.entity_factory.deserialize_voice_state.return_value
         assert event.old_state is mock_old_voice_state
diff --git a/tests/hikari/test_channels.py b/tests/hikari/test_channels.py
index 9ad82d40..b398a731 100644
--- a/tests/hikari/test_channels.py
+++ b/tests/hikari/test_channels.py
@@ -65,24 +65,24 @@ async def test_fetch_webhook(self, mock_app):
         assert result is mock_app.rest.fetch_webhook.return_value
         mock_app.rest.fetch_webhook.assert_awaited_once_with(54123123)
 
-    def test_channel(self, mock_app):
+    def test_get_channel(self, mock_app):
         mock_channel = mock.Mock(spec=channels.GuildNewsChannel)
         mock_app.cache.get_guild_channel = mock.Mock(return_value=mock_channel)
         follow = channels.ChannelFollow(
             webhook_id=snowflakes.Snowflake(993883), app=mock_app, channel_id=snowflakes.Snowflake(696969)
         )
 
-        result = follow.channel
+        result = follow.get_channel()
 
         assert result is mock_channel
         mock_app.cache.get_guild_channel.assert_called_once_with(696969)
 
-    def test_channel_when_no_cache_trait(self):
+    def test_get_channel_when_no_cache_trait(self):
         follow = channels.ChannelFollow(
             webhook_id=snowflakes.Snowflake(993883), app=object(), channel_id=snowflakes.Snowflake(696969)
         )
 
-        assert follow.channel is None
+        assert follow.get_channel() is None
 
 
 class TestPermissionOverwrite:
diff --git a/tests/hikari/test_guilds.py b/tests/hikari/test_guilds.py
index ca78a70e..e921371c 100644
--- a/tests/hikari/test_guilds.py
+++ b/tests/hikari/test_guilds.py
@@ -326,58 +326,56 @@ def test_mention_property_when_no_nickname(self, model, mock_user):
         model.nickname = None
         assert model.mention == mock_user.mention
 
-    def test_roles(self, model):
+    def test_get_roles(self, model):
         role1 = mock.Mock(id=321, position=2)
         role2 = mock.Mock(id=654, position=1)
-        mock_cache_view = {321: role1, 654: role2}
-        model.user.app.cache.get_roles_view_for_guild.return_value = mock_cache_view
+        model.user.app.cache.get_role.side_effect = [role1, role2]
         model.role_ids = [321, 654]
 
-        assert model.roles == [role1, role2]
+        assert model.get_roles() == [role1, role2]
 
-        model.user.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
+        model.user.app.cache.get_role.assert_has_calls([mock.call(321), mock.call(654)])
 
-    def test_roles_when_role_ids_not_in_cache(self, model):
-        role1 = mock.Mock(id=123, position=2)
-        role2 = mock.Mock(id=456, position=1)
-        mock_cache_view = {123: role1, 456: role2}
-        model.user.app.cache.get_roles_view_for_guild.return_value = mock_cache_view
+    def test_get_roles_when_role_ids_not_in_cache(self, model):
+        role = mock.Mock(id=456, position=1)
+        model.user.app.cache.get_role.side_effect = [None, role]
         model.role_ids = [321, 456]
 
-        assert model.roles == [role2]
+        assert model.get_roles() == [role]
 
-        model.user.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
+        model.user.app.cache.get_role.assert_has_calls([mock.call(321), mock.call(456)])
 
-    def test_roles_when_empty_cache(self, model):
-        model.user.app.cache.get_roles_view_for_guild.return_value = {}
+    def test_get_roles_when_empty_cache(self, model):
+        model.role_ids = [132, 432]
+        model.user.app.cache.get_role.side_effect = [None, None]
 
-        assert model.roles == []
+        assert model.get_roles() == []
 
-        model.user.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
+        model.user.app.cache.get_role.assert_has_calls([mock.call(132), mock.call(432)])
 
-    def test_roles_when_no_cache_trait(self, model):
+    def test_get_roles_when_no_cache_trait(self, model):
         model.user.app = object()
 
-        assert model.roles == []
+        assert model.get_roles() == []
 
-    def test_top_role(self, model):
+    def test_get_top_role(self, model):
         role1 = mock.Mock(id=321, position=2)
         role2 = mock.Mock(id=654, position=1)
 
-        with mock.patch.object(guilds.Member, "roles", new=[role1, role2]):
-            assert model.top_role is role1
+        with mock.patch.object(guilds.Member, "get_roles", return_value=[role1, role2]):
+            assert model.get_top_role() is role1
 
-    def test_top_role_when_roles_is_empty(self, model):
-        with mock.patch.object(guilds.Member, "roles", new=[]):
-            assert model.top_role is None
+    def test_get_top_role_when_roles_is_empty(self, model):
+        with mock.patch.object(guilds.Member, "get_roles", return_value=[]):
+            assert model.get_top_role() is None
 
-    def test_presence(self, model):
-        assert model.presence is model.user.app.cache.get_presence.return_value
+    def test_get_presence(self, model):
+        assert model.get_presence() is model.user.app.cache.get_presence.return_value
         model.user.app.cache.get_presence.assert_called_once_with(456, 123)
 
-    def test_presence_when_no_cache_trait(self, model):
+    def test_get_presence_when_no_cache_trait(self, model):
         model.user.app = object()
-        assert model.presence is None
+        assert model.get_presence() is None
 
 
 class TestPartialGuild:
@@ -766,37 +764,69 @@ def model(self, mock_app):
             system_channel_flags=guilds.GuildSystemChannelFlag.SUPPRESS_PREMIUM_SUBSCRIPTION,
         )
 
-    def test_channels(self, model):
-        assert model.channels is model.app.cache.get_guild_channels_view_for_guild.return_value
+    def test_get_channels(self, model):
+        assert model.get_channels() is model.app.cache.get_guild_channels_view_for_guild.return_value
         model.app.cache.get_guild_channels_view_for_guild.assert_called_once_with(123)
 
-    def test_channels_when_no_cache_trait(self, model):
+    def test_get_channels_when_no_cache_trait(self, model):
         model.app = object()
-        assert model.channels == {}
+        assert model.get_channels() == {}
 
-    def test_members(self, model):
-        assert model.members is model.app.cache.get_members_view_for_guild.return_value
+    def test_get_members(self, model):
+        assert model.get_members() is model.app.cache.get_members_view_for_guild.return_value
         model.app.cache.get_members_view_for_guild.assert_called_once_with(123)
 
-    def test_members_when_no_cache_trait(self, model):
+    def test_get_members_when_no_cache_trait(self, model):
         model.app = object()
-        assert model.members == {}
+        assert model.get_members() == {}
 
-    def test_presences(self, model):
-        assert model.presences is model.app.cache.get_presences_view_for_guild.return_value
+    def test_get_presences(self, model):
+        assert model.get_presences() is model.app.cache.get_presences_view_for_guild.return_value
         model.app.cache.get_presences_view_for_guild.assert_called_once_with(123)
 
-    def test_presences_when_no_cache_trait(self, model):
+    def test_get_presences_when_no_cache_trait(self, model):
         model.app = object()
-        assert model.presences == {}
+        assert model.get_presences() == {}
 
-    def test_voice_states(self, model):
-        assert model.voice_states is model.app.cache.get_voice_states_view_for_guild.return_value
+    def test_get_voice_states(self, model):
+        assert model.get_voice_states() is model.app.cache.get_voice_states_view_for_guild.return_value
         model.app.cache.get_voice_states_view_for_guild.assert_called_once_with(123)
 
-    def test_voice_states_when_no_cache_trait(self, model):
+    def test_get_voice_states_when_no_cache_trait(self, model):
+        model.app = object()
+        assert model.get_voice_states() == {}
+
+    def test_get_emojis(self, model):
+        assert model.get_emojis() is model.app.cache.get_emojis_view_for_guild.return_value
+        model.app.cache.get_emojis_view_for_guild.assert_called_once_with(123)
+
+    def test_emojis_when_no_cache_trait(self, model):
+        model.app = object()
+        assert model.get_emojis() == {}
+
+    def test_roles(self, model):
+        assert model.get_roles() is model.app.cache.get_roles_view_for_guild.return_value
+        model.app.cache.get_roles_view_for_guild.assert_called_once_with(123)
+
+    def test_get_roles_when_no_cache_trait(self, model):
         model.app = object()
-        assert model.voice_states == {}
+        assert model.get_roles() == {}
+
+    def test_get_emoji(self, model):
+        assert model.get_emoji(456) is model.app.cache.get_emoji.return_value
+        model.app.cache.get_emoji.assert_called_once_with(456)
+
+    def test_get_emoji_when_no_cache_trait(self, model):
+        model.app = object()
+        assert model.get_emoji(456) is None
+
+    def test_get_role(self, model):
+        assert model.get_role(456) is model.app.cache.get_role.return_value
+        model.app.cache.get_role.assert_called_once_with(456)
+
+    def test_get_role_when_no_cache_trait(self, model):
+        model.app = object()
+        assert model.get_role(456) is None
 
     def test_splash_url(self, model):
         splash = object()
@@ -988,16 +1018,21 @@ def test_get_my_member_when_not_shardaware(self, model):
         assert model.get_my_member() is None
 
     def test_get_my_member_when_no_me(self, model):
-        model.app.me = None
+        model.app.get_me = mock.Mock(return_value=None)
+
         assert model.get_my_member() is None
 
+        model.app.get_me.assert_called_once_with()
+
     def test_get_my_member(self, model):
-        model.app.me = mock.Mock(id=123)
+        model.app.get_me = mock.Mock()
+        model.app.get_me.return_value.id = 123
 
         with mock.patch.object(guilds.Guild, "get_member") as get_member:
             assert model.get_my_member() is get_member.return_value
 
         get_member.assert_called_once_with(123)
+        model.app.get_me.assert_called_once_with()
 
 
 class TestRestGuild:
@@ -1040,104 +1075,3 @@ def model(self, mock_app):
             max_members=100,
             nsfw_level=guilds.GuildNSFWLevel.AGE_RESTRICTED,
         )
-
-    def test_get_emoji(self, model):
-        emoji = object()
-        model._emojis = {snowflakes.Snowflake(123): emoji}
-
-        assert model.get_emoji(123) is emoji
-
-    def test_get_role(self, model):
-        role = object()
-        model._roles = {snowflakes.Snowflake(123): role}
-
-        assert model.get_role(123) is role
-
-
-class TestGatewayGuild:
-    @pytest.fixture()
-    def model(self, mock_app):
-        return guilds.GatewayGuild(
-            app=mock_app,
-            id=snowflakes.Snowflake(123),
-            splash_hash="splash_hash",
-            discovery_splash_hash="discovery_splash_hash",
-            banner_hash="banner_hash",
-            icon_hash="icon_hash",
-            features=[guilds.GuildFeature.ANIMATED_ICON],
-            name="some guild",
-            application_id=snowflakes.Snowflake(9876),
-            afk_channel_id=snowflakes.Snowflake(1234),
-            afk_timeout=datetime.timedelta(seconds=60),
-            default_message_notifications=guilds.GuildMessageNotificationsLevel.ONLY_MENTIONS,
-            description=None,
-            explicit_content_filter=guilds.GuildExplicitContentFilterLevel.ALL_MEMBERS,
-            is_widget_enabled=False,
-            max_video_channel_users=10,
-            mfa_level=guilds.GuildMFALevel.NONE,
-            owner_id=snowflakes.Snowflake(1111),
-            preferred_locale="en-GB",
-            premium_subscription_count=12,
-            premium_tier=guilds.GuildPremiumTier.TIER_3,
-            public_updates_channel_id=None,
-            rules_channel_id=None,
-            system_channel_id=None,
-            vanity_url_code="yeet",
-            verification_level=guilds.GuildVerificationLevel.VERY_HIGH,
-            widget_channel_id=None,
-            system_channel_flags=guilds.GuildSystemChannelFlag.SUPPRESS_PREMIUM_SUBSCRIPTION,
-            is_large=True,
-            joined_at=None,
-            member_count=1,
-            nsfw_level=guilds.GuildNSFWLevel.AGE_RESTRICTED,
-        )
-
-    @pytest.fixture()
-    def channels(self):
-        return {
-            4321: mock.Mock(channels_.GuildTextChannel),
-            3321: mock.Mock(channels_.GuildNewsChannel),
-            2321: mock.Mock(channels_.GuildStoreChannel),
-            5321: mock.Mock(channels_.GuildVoiceChannel),
-            6321: mock.Mock(channels_.GuildStageChannel),
-        }
-
-    def test_channels(self, model):
-        assert model.channels is model.app.cache.get_guild_channels_view_for_guild.return_value
-        model.app.cache.get_guild_channels_view_for_guild.assert_called_once_with(123)
-
-    def test_channels_when_no_cache_trait(self, model):
-        model.app = object()
-        assert model.channels == {}
-
-    def test_emojis(self, model):
-        assert model.emojis is model.app.cache.get_emojis_view_for_guild.return_value
-        model.app.cache.get_emojis_view_for_guild.assert_called_once_with(123)
-
-    def test_emojis_when_no_cache_trait(self, model):
-        model.app = object()
-        assert model.emojis == {}
-
-    def test_roles(self, model):
-        assert model.roles is model.app.cache.get_roles_view_for_guild.return_value
-        model.app.cache.get_roles_view_for_guild.assert_called_once_with(123)
-
-    def test_roles_when_no_cache_trait(self, model):
-        model.app = object()
-        assert model.roles == {}
-
-    def test_get_emoji(self, model):
-        assert model.get_emoji(456) is model.app.cache.get_emoji.return_value
-        model.app.cache.get_emoji.assert_called_once_with(456)
-
-    def test_get_emoji_when_no_cache_trait(self, model):
-        model.app = object()
-        assert model.get_emoji(456) is None
-
-    def test_get_role(self, model):
-        assert model.get_role(456) is model.app.cache.get_role.return_value
-        model.app.cache.get_role.assert_called_once_with(456)
-
-    def test_get_role_when_no_cache_trait(self, model):
-        model.app = object()
-        assert model.get_role(456) is None