hardware layer: you have 2 interfaces: no need for addressing, just dump everything on the wire
link layer: you have more than 2 interfaces all connected together: give each interface an address (MAC address, random assignment)
internet layer: you have a lot more than 2 interfaces all connected together: give each interface an address (IP address, heirarchical assignment)
transport layer: you want to talk to a specific process listening on an interface: give each process a port, optionally handle streaming (ex: UDP/TCP port)
transport layer II: you want to talk to a specific resource set in a process (and/or you want security): give each resource set a domain name (ex: DTLS/TLS/QUIC authority, SNI)
application layer: you want to talk to a specific resource in your resource set: give each resource a path, optionally duplicate TLS authority, optionally add method (ex: HTTP path)
application layer II: whatever you built on top of HTTP: (ex: JSON-RPC, gRPC, GrapQL, ...)
ex raw DNS works directly on top of the transport layer, has no concept of host/domain names,
drill @your.resolver query
does a lookup first for you resolver where the query is then sent,
so you can't really multiplex multiple DNS resolvers on a single port.