use (service, name, ...fns) {
    if (typeof service === 'function') {
      const isFunction = typeof name === 'function'

      for (const serviceName in this.data) {
        const _service = this.data[serviceName]

        if (isFunction) {
          _service.middleware = _service.middleware.concat(service, name, fns)
        } else {
          _service.middleware = _service.middleware.concat(service, fns)
        }
      }
    } else if (typeof service === 'object') {
      // we have object notation
      const testKey = Object.keys(service)[0]
      if (typeof service[testKey] === 'function' || Array.isArray(service[testKey])) {
        // first property of object is a function or array
        // that means we have service-level middleware of RPC handlers

        for (const key in service) {
          // lets try to match the key to any service name first
          const val = service[key]
          const serviceName = this._getMatchingServiceName(key)

          if (serviceName) {
            // we have a matching service
            // lets add service-level middleware to that service
            this.data[serviceName].middleware.push(val)
          } else {
            // we need to find the matching function to set it as handler
            const { serviceName, methodName } = this._getMatchingCall(key)

            if (serviceName && methodName) {
              if (typeof val === 'function') {
                this.use(serviceName, methodName, val)
              } else {
                this.use(serviceName, methodName, ...val)
              }
            } else {
              throw new TypeError(`Unknown method: ${key}`)
            }
          }
        }
      } else if (typeof service[testKey] === 'object') {
        for (const serviceName in service) {
          for (const middlewareName in service[serviceName]) {
            const middleware = service[serviceName][middlewareName]
            if (typeof middleware === 'function') {
              this.use(serviceName, middlewareName, middleware)
            } else if (Array.isArray(middleware)) {
              this.use(serviceName, middlewareName, ...middleware)
            } else {
              throw new TypeError(`Handler for ${middlewareName} is not a function or array`)
            }
          }
        }
      } else {
        throw new TypeError(`Invalid type for handler for ${testKey}`)
      }
    } else {
      if (typeof name !== 'string') {
        // name is a function pre-pand it to fns
        fns.unshift(name)

        // service param can either be a service name or a function name
        // first lets try to match a service

        const serviceName = this._getMatchingServiceName(service)
        if (serviceName) {
          // we have a matching service
          // lets add service-level middleware to that service
          const sd = this.data[serviceName]
          sd.middleware = sd.middleware.concat(fns)

          return
        } else {
          // service param is a function name
          // lets try to find the matching call and service

          const { serviceName, methodName } = this._getMatchingCall(service)
          if (!serviceName || !methodName) {
            throw new Error(`Unknown identifier: ${service}`)
          }

          this.use(serviceName, methodName, ...fns)

          return
        }
      }

      // we have a string service, and string name

      const serviceName = this._getMatchingServiceName(service)

      if (!serviceName) {
        throw new Error(`Unknown service ${service}`)
      }

      const sd = this.data[serviceName]

      let methodName

      for (const _methodName in sd.methods) {
        if (this._getMatchingHandlerName(sd.methods[_methodName], _methodName, name)) {
          methodName = _methodName
          break
        }
      }

      if (!methodName) {
        throw new Error(`Unknown method ${name} for service ${serviceName}`)
      }

      if (sd.handlers[methodName]) {
        throw new Error(`Handler for ${name} already defined for service ${serviceName}`)
      }

      sd.handlers[methodName] = sd.middleware.concat(fns)
    }
  }
Define middleware and handlers.
@param {String|Object} service Service name
@param {String|Function} name RPC name
@param {Function|Array} fns - Middleware and/or handler

@example <caption>Define handler for RPC function 'getUser' in first service we find that has that call name.</caption>
app.use('getUser', getUser)

@example <caption>Define handler with middleware for RPC function 'getUser' in first service we find that has that call name.</caption>
app.use('getUser', mw1, mw2, getUser)

@example <caption>Define handler with middleware for RPC function 'getUser' in service 'MyService'. We pick first service that matches the name.</caption>
app.use('MyService', 'getUser', mw1, mw2, getUser)

@example <caption>Define handler with middleware for rpc function 'getUser' in service 'MyService' with full package name.</caption>
app.use('myorg.myapi.v1.MyService', 'getUser', mw1, mw2, getUser)

@example <caption>Using destructuring define handlers for rpc functions 'getUser' and 'deleteUser'. Here we would match the first service that has a `getUser` RPC method.</caption>
app.use({ getUser, deleteUser })

@example <caption>Apply middleware to all handlers for a given service. We match first service that has the given name.</caption>
app.use('MyService', mw1)

@example <caption>Apply middleware to all handlers for a given service using full namespaced package name.</caption>
app.use('myorg.myapi.v1.MyService', mw1)

@example <caption>Using destructuring define handlers for RPC functions 'getUser' and 'deleteUser'. We match first service that has the given name.</caption>
// deleteUser has middleware mw1 and mw2
app.use({ MyService: { getUser, deleteUser: [mw1, mw2, deleteUser] } })

@example <caption>Using destructuring define handlers for RPC functions 'getUser' and 'deleteUser'.</caption>
// deleteUser has middleware mw1 and mw2
app.use({ 'myorg.myapi.v1.MyService': { getUser, deleteUser: [mw1, mw2, deleteUser] } })

@example <caption>Multiple services using object notation.</caption>
app.use(mw1) // global for all services
app.use('MyService', mw2) // applies to first matched service named 'MyService'
app.use({
  'myorg.myapi.v1.MyService': { // matches MyService
    sayGoodbye: handler1, // has mw1, mw2
    sayHello: [ mw3, handler2 ] // has mw1, mw2, mw3
  },
  'myorg.myapi.v1.MyOtherService': {
    saySomething: handler3 // only has mw1
  }
})