diff --git a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
index 651b4827ca32154c74d9885fbd855f891ecddaf9..c1cf53d8df1f6433f27e1a91c36b49a1c5a521c5 100644
--- a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
+++ b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh
@@ -8,23 +8,45 @@
 /*!
  * \file
  * \ingroup NavierStokesModel
- * \copydoc Dumux::NavierStokesMomentumBoundaryFluxHelper
+ * \copydoc Dumux::NavierStokesMomentumBoundaryFlux
  */
-#ifndef DUMUX_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
-#define DUMUX_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
+#ifndef DUMUX_FREEFLOW_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
+#define DUMUX_FREEFLOW_NAVIERSTOKES_MOMENTUM_BOUNDARY_FLUXHELPER_HH
 
 #include <dumux/common/math.hh>
 #include <dumux/common/parameters.hh>
 #include <dumux/discretization/method.hh>
 #include <dumux/freeflow/navierstokes/momentum/velocitygradients.hh>
+#include <dumux/freeflow/navierstokes/slipcondition.hh>
 
 namespace Dumux {
 
 /*!
  * \ingroup NavierStokesModel
- * \brief Struct containing flux helper functions to be used in the momentum problem's Neumann function.
+ * \brief Class to compute the boundary flux for the momentum balance of the Navier-Stokes model
+ * This helper class is typically used in the Neumann function of the momentum problem.
+ *
+ * \tparam DiscretizationMethod The discretization method used for the momentum problem
+ * \tparam SlipVelocityPolicy The policy to compute the slip velocity at the boundary
+ *
+ * The slip velocity policy is a class with a static member function `velocity`
+ * that computes the slip velocity at the boundary.
  */
-struct NavierStokesMomentumBoundaryFluxHelper
+template<class DiscretizationMethod, class SlipVelocityPolicy = void>
+struct NavierStokesMomentumBoundaryFlux;
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Class to compute the boundary flux for the momentum balance of the Navier-Stokes model
+ * This helper class is typically used in the Neumann function of the momentum problem.
+ *
+ * \tparam SlipVelocityPolicy The policy to compute the slip velocity at the boundary
+ *
+ * The slip velocity policy is a class with a static member function `velocity`
+ * that computes the slip velocity at the boundary.
+ */
+template<class SlipVelocityPolicy>
+struct NavierStokesMomentumBoundaryFlux<DiscretizationMethods::FCStaggered, SlipVelocityPolicy>
 {
     /*!
      * \brief Returns the momentum flux a fixed-pressure boundary.
@@ -50,7 +72,6 @@ struct NavierStokesMomentumBoundaryFluxHelper
                                           const bool zeroNormalVelocityGradient = true)
     {
         // TODO density upwinding?
-        static_assert(FVElementGeometry::GridGeometry::discMethod == DiscretizationMethods::fcstaggered);
         using MomentumFlux = typename Problem::MomentumFluxType;
         constexpr std::size_t dim = static_cast<std::size_t>(FVElementGeometry::GridGeometry::GridView::dimension);
         static_assert(
@@ -203,7 +224,6 @@ struct NavierStokesMomentumBoundaryFluxHelper
                                          const ElementFluxVariablesCache& elemFluxVarsCache,
                                          const TangentialVelocityGradient& tangentialVelocityGradient = TangentialVelocityGradient(0.0))
     {
-        static_assert(FVElementGeometry::GridGeometry::discMethod == DiscretizationMethods::fcstaggered);
         using MomentumFlux = typename Problem::MomentumFluxType;
         constexpr std::size_t dim = static_cast<std::size_t>(FVElementGeometry::GridGeometry::GridView::dimension);
         static_assert(
@@ -239,61 +259,67 @@ struct NavierStokesMomentumBoundaryFluxHelper
             *                                                   :: staggered half-control-volume (neighbor element)
             *
             */
-            const Scalar velI = elemVolVars[scvf.insideScvIdx()].velocity();
 
-            // viscous terms
-            const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
-            const Scalar distance = (scv.dofPosition()- scvf.ipGlobal()).two_norm();
-            const Scalar velGradJI = [&]
+            if constexpr (!std::is_void_v<SlipVelocityPolicy>)
             {
-                if (elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
-                    return StaggeredVelocityGradients::velocityGradJI(fvGeometry, scvf, elemVolVars);
-                else
-                    return tangentialVelocityGradient;
-            }();
+                const Scalar velI = elemVolVars[scvf.insideScvIdx()].velocity();
 
-            const Scalar slipVelocity = problem.beaversJosephVelocity(fvGeometry, scvf, elemVolVars, velGradJI)[scv.dofAxis()]; // TODO rename to slipVelocity
-            const Scalar velGradIJ = (slipVelocity - velI) / distance * scvf.directionSign();
+                // viscous terms
+                const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
+                const Scalar distance = (scv.dofPosition()- scvf.ipGlobal()).two_norm();
+                const Scalar velGradJI = [&]
+                {
+                    if (elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
+                        return StaggeredVelocityGradients::velocityGradJI(fvGeometry, scvf, elemVolVars);
+                    else
+                        return tangentialVelocityGradient;
+                }();
 
-            flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
+                const Scalar slipVelocity = SlipVelocityPolicy::velocity(problem, fvGeometry, scvf, elemVolVars, velGradJI)[scv.dofAxis()];
+                const Scalar velGradIJ = (slipVelocity - velI) / distance * scvf.directionSign();
 
-            // advective terms
-            if (problem.enableInertiaTerms())
-            {
-                // transporting velocity corresponds to velJ
-                const auto transportingVelocity = [&]()
+                flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
+
+                // advective terms
+                if (problem.enableInertiaTerms())
                 {
-                    const auto innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
+                    // transporting velocity corresponds to velJ
+                    const auto transportingVelocity = [&]()
+                    {
+                        const auto innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
 
-                    if (!elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
-                        return innerTransportingVelocity; // fallback
+                        if (!elemVolVars.hasVolVars(orthogonalScvf.outsideScvIdx()))
+                            return innerTransportingVelocity; // fallback
 
-                    const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
+                        const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
 
-                    // if the orthogonal scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
-                    if (orthogonalScvf.boundary())
-                        return outerTransportingVelocity;
+                        // if the orthogonal scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
+                        if (orthogonalScvf.boundary())
+                            return outerTransportingVelocity;
 
-                    static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate?
-                    if (useOldScheme)
-                        return innerTransportingVelocity;
-                    else
-                    {
-                        // average the transporting velocity by weighting with the scv volumes
-                        const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume();
-                        const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume();
-                        const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
-                        return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume);
-                     }
-                }();
+                        static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate?
+                        if (useOldScheme)
+                            return innerTransportingVelocity;
+                        else
+                        {
+                            // average the transporting velocity by weighting with the scv volumes
+                            const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume();
+                            const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume();
+                            const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity();
+                            return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume);
+                        }
+                    }();
 
-                // Do not use upwinding here but directly take the slip velocity located on the boundary. Upwinding with a weight of 0.5
-                // would actually prevent second order grid convergence.
-                const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
-                const auto transportedMomentum = slipVelocity * rho.second;
+                    // Do not use upwinding here but directly take the slip velocity located on the boundary. Upwinding with a weight of 0.5
+                    // would actually prevent second order grid convergence.
+                    const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
+                    const auto transportedMomentum = slipVelocity * rho.second;
 
-                flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
+                    flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
+                }
             }
+            else
+                DUNE_THROW(Dune::InvalidStateException, "SlipVelocityPolicy needs to be specified as template argument");
         }
         else if (scv.boundary() && problem.onSlipBoundary(fvGeometry, fvGeometry.frontalScvfOnBoundary(scv)))
         {
@@ -317,67 +343,78 @@ struct NavierStokesMomentumBoundaryFluxHelper
             *                            |   |   |
             *
             */
-            const Scalar velJ = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
-
-            // viscous terms
-            const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
-            const Scalar distance = (fvGeometry.scv(orthogonalScvf.insideScvIdx()).dofPosition()- scvf.ipGlobal()).two_norm();
 
-            const Scalar velGradIJ = [&]
+            if constexpr (!std::is_void_v<SlipVelocityPolicy>)
             {
-                if (elemVolVars.hasVolVars(scvf.outsideScvIdx()))
-                    return StaggeredVelocityGradients::velocityGradIJ(fvGeometry, scvf, elemVolVars);
-                else
-                    return tangentialVelocityGradient;
-            }();
+                const Scalar velJ = elemVolVars[orthogonalScvf.insideScvIdx()].velocity();
 
-            const Scalar slipVelocity = problem.beaversJosephVelocity(fvGeometry, orthogonalScvf, elemVolVars, velGradIJ)[scvf.normalAxis()]; // TODO rename to slipVelocity
-            const Scalar velGradJI = (slipVelocity - velJ) / distance * orthogonalScvf.directionSign();
+                // viscous terms
+                const Scalar mu = problem.effectiveViscosity(fvGeometry.element(), fvGeometry, scvf);
+                const Scalar distance = (fvGeometry.scv(orthogonalScvf.insideScvIdx()).dofPosition()- scvf.ipGlobal()).two_norm();
 
-            flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
+                const Scalar velGradIJ = [&]
+                {
+                    if (elemVolVars.hasVolVars(scvf.outsideScvIdx()))
+                        return StaggeredVelocityGradients::velocityGradIJ(fvGeometry, scvf, elemVolVars);
+                    else
+                        return tangentialVelocityGradient;
+                }();
 
-            // advective terms
-            if (problem.enableInertiaTerms())
-            {
-                // transporting velocity corresponds to velJ
-                const auto transportingVelocity = slipVelocity;
+                const Scalar slipVelocity = SlipVelocityPolicy::velocity(problem, fvGeometry, orthogonalScvf, elemVolVars, velGradIJ)[scvf.normalAxis()];
+                const Scalar velGradJI = (slipVelocity - velJ) / distance * orthogonalScvf.directionSign();
 
-                // if the scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
-                if (scvf.boundary() && elemVolVars.hasVolVars(scvf.outsideScvIdx()))
-                {
-                    flux[scv.dofAxis()] += problem.density(fvGeometry.element(), scv)
-                                        * elemVolVars[scvf.outsideScvIdx()].velocity() * transportingVelocity * scvf.directionSign(); // TODO revise density
-                    return flux;
-                }
+                flux[scv.dofAxis()] -= (mu * (velGradIJ + velGradJI))*scvf.directionSign();
 
-                const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
-                const auto outerVelocity = [&]
+                // advective terms
+                if (problem.enableInertiaTerms())
                 {
-                    if (!elemVolVars.hasVolVars(scvf.outsideScvIdx()))
-                        return innerVelocity; // fallback
-                    else
-                        return elemVolVars[scvf.outsideScvIdx()].velocity();
-                }();
+                    // transporting velocity corresponds to velJ
+                    const auto transportingVelocity = slipVelocity;
+
+                    // if the scvf lies on a boundary and if there are outside volvars, we assume that these come from a Dirichlet condition
+                    if (scvf.boundary() && elemVolVars.hasVolVars(scvf.outsideScvIdx()))
+                    {
+                        flux[scv.dofAxis()] += problem.density(fvGeometry.element(), scv)
+                                            * elemVolVars[scvf.outsideScvIdx()].velocity() * transportingVelocity * scvf.directionSign(); // TODO revise density
+                        return flux;
+                    }
 
-                const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
-                const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity);
+                    const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity();
+                    const auto outerVelocity = [&]
+                    {
+                        if (!elemVolVars.hasVolVars(scvf.outsideScvIdx()))
+                            return innerVelocity; // fallback
+                        else
+                            return elemVolVars[scvf.outsideScvIdx()].velocity();
+                    }();
+
+                    const auto rho = problem.insideAndOutsideDensity(fvGeometry.element(), fvGeometry, scvf);
+                    const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity);
 
-                const auto insideMomentum = innerVelocity * rho.first;
-                const auto outsideMomentum = outerVelocity * rho.second;
+                    const auto insideMomentum = innerVelocity * rho.first;
+                    const auto outsideMomentum = outerVelocity * rho.second;
 
-                static const auto upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
+                    static const auto upwindWeight = getParamFromGroup<Scalar>(problem.paramGroup(), "Flux.UpwindWeight");
 
-                const auto transportedMomentum =  selfIsUpstream ? (upwindWeight * insideMomentum + (1.0 - upwindWeight) * outsideMomentum)
-                                                                    : (upwindWeight * outsideMomentum + (1.0 - upwindWeight) * insideMomentum);
+                    const auto transportedMomentum =  selfIsUpstream ? (upwindWeight * insideMomentum + (1.0 - upwindWeight) * outsideMomentum)
+                                                                        : (upwindWeight * outsideMomentum + (1.0 - upwindWeight) * insideMomentum);
 
-                flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
+                    flux[scv.dofAxis()] += transportingVelocity * transportedMomentum * scvf.directionSign();
+                }
             }
+            else
+                DUNE_THROW(Dune::InvalidStateException, "SlipVelocityPolicy needs to be specified as template argument");
         }
 
         return flux;
     }
 };
 
+using NavierStokesMomentumBoundaryFluxHelper
+    [[deprecated("Replace with implementation class `NavierStokesMomentumBoundaryFlux`with template arguments `DiscretizationMethod` and `SlipVelocityPolicy`. This will be removed after 3.9.")]]
+    = NavierStokesMomentumBoundaryFlux<DiscretizationMethods::FCStaggered,
+                                       NavierStokesSlipVelocity<DiscretizationMethods::FCStaggered, NavierStokes::SlipConditions::BJ>>;
+
 } // end namespace Dumux
 
 #endif
diff --git a/dumux/freeflow/navierstokes/momentum/problem.hh b/dumux/freeflow/navierstokes/momentum/problem.hh
index 6a39312e6f6350afa238984990f2d9d565420698..3df164689440324574c54359478b6974014d0f25 100644
--- a/dumux/freeflow/navierstokes/momentum/problem.hh
+++ b/dumux/freeflow/navierstokes/momentum/problem.hh
@@ -427,6 +427,7 @@ public:
      * \brief Returns the intrinsic permeability of required as input parameter for the Beavers-Joseph-Saffman boundary condition
      * This member function must be overloaded in the problem implementation, if the BJS boundary condition is used.
      */
+    [[deprecated("Will be removed after release 3.9.")]]
     Scalar permeability(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
     {
         DUNE_THROW(Dune::NotImplemented,
@@ -439,6 +440,7 @@ public:
      * \brief Returns the alpha value required as input parameter for the Beavers-Joseph-Saffman boundary condition
      * This member function must be overloaded in the problem implementation, if the BJS boundary condition is used.
      */
+    [[deprecated("Will be removed after release 3.9. Implement betaBJ instead. ")]]
     Scalar alphaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
     {
         DUNE_THROW(Dune::NotImplemented,
@@ -450,9 +452,11 @@ public:
     /*!
      * \brief Returns the beta value which is the alpha value divided by the square root of the (scalar-valued) interface permeability.
      */
+    [[deprecated("Needs to be implemented in test problem. Will be removed after release 3.9.")]]
     Scalar betaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, const GlobalPosition& tangentialVector) const
     {
-        const Scalar interfacePermeability = interfacePermeability_(fvGeometry, scvf, tangentialVector);
+        const auto& K = asImp_().permeability(fvGeometry, scvf);
+        const auto interfacePermeability = vtmv(tangentialVector, K, tangentialVector);
         using std::sqrt;
         return asImp_().alphaBJ(fvGeometry, scvf) / sqrt(interfacePermeability);
     }
@@ -460,6 +464,7 @@ public:
     /*!
      * \brief Returns the velocity in the porous medium (which is 0 by default according to Saffmann).
      */
+    [[deprecated("Needs to be implemented in test problem. Will be removed after release 3.9.")]]
     VelocityVector porousMediumVelocity(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
     {
         return VelocityVector(0.0);
@@ -469,6 +474,7 @@ public:
      * \brief Returns the slip velocity at a porous boundary based on the Beavers-Joseph(-Saffman) condition.
      * \note This only returns a vector filled with one component of the slip velocity (corresponding to the dof axis of the scv the svf belongs to)
      */
+    [[deprecated("Will be removed after release 3.9.")]]
     const VelocityVector beaversJosephVelocity(const FVElementGeometry& fvGeometry,
                                                const SubControlVolumeFace& scvf,
                                                const ElementVolumeVariables& elemVolVars,
@@ -511,19 +517,6 @@ public:
     }
 
 private:
-    //! Returns a scalar permeability value at the coupling interface
-    template<class Scvf>
-    Scalar interfacePermeability_(const FVElementGeometry& fvGeometry, const Scvf& scvf, const GlobalPosition& tangentialVector) const
-    {
-        const auto& K = asImp_().permeability(fvGeometry, scvf);
-
-        // use t*K*t for permeability tensors
-        if constexpr (Dune::IsNumber<std::decay_t<decltype(K)>>::value)
-            return K;
-        else
-            return vtmv(tangentialVector, K, tangentialVector);
-    }
-
     //! Returns the implementation of the problem (i.e. static polymorphism)
     Implementation& asImp_()
     { return *static_cast<Implementation *>(this); }
diff --git a/dumux/freeflow/navierstokes/slipcondition.hh b/dumux/freeflow/navierstokes/slipcondition.hh
new file mode 100644
index 0000000000000000000000000000000000000000..5888d263c84cd4ccc8d8d6bd969b1cd36cdc287e
--- /dev/null
+++ b/dumux/freeflow/navierstokes/slipcondition.hh
@@ -0,0 +1,126 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+/*!
+ * \file
+ * \ingroup NavierStokesModel
+ * \brief Navier Stokes slip condition
+ */
+#ifndef DUMUX_FREEFLOW_NAVIERSTOKES_SLIPCONDITION_HH
+#define DUMUX_FREEFLOW_NAVIERSTOKES_SLIPCONDITION_HH
+
+#include <dumux/common/tag.hh>
+#include <dumux/discretization/method.hh>
+
+namespace Dumux::NavierStokes::SlipConditions {
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Tag for the Beavers-Joseph slip condition
+ */
+struct BJ : public Utility::Tag<BJ> {
+    static std::string name() { return "Beavers-Joseph"; }
+};
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Tag for the Beavers-Joseph-Saffman slip condition
+ */
+struct BJS : public Utility::Tag<BJS> {
+    static std::string name() { return "Beavers-Joseph-Saffman"; }
+};
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Tag for the Beavers-Joseph slip condition
+ */
+inline constexpr BJ bj{};
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Tag for the Beavers-Joseph-Saffman slip condition
+ */
+inline constexpr BJS bjs{};
+
+} // end namespace Dumux::NavierStokes::SlipConditions
+
+namespace Dumux {
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Navier Stokes slip velocity policy
+ */
+template<class DiscretizationMethod, class SlipCondition>
+class NavierStokesSlipVelocity;
+
+/*!
+ * \ingroup NavierStokesModel
+ * \brief Navier Stokes slip velocity helper for fcstaggered discretization
+ *
+ * For now, this class implements the Beavers-Joseph or the Beavers-Joseph-Saffman condition
+ * which models the slip velocity at a porous boundary. The condition is chosen by passing
+ * the corresponding SlipCondition tag to the class.
+ */
+template<class SlipCondition>
+struct NavierStokesSlipVelocity<DiscretizationMethods::FCStaggered, SlipCondition>
+{
+    static constexpr SlipCondition slipCondition{};
+
+    /*!
+     * \brief Returns the slip velocity at a porous boundary based on the Beavers-Joseph(-Saffman) condition.
+     * \note This only returns a vector filled with one component of the slip velocity (corresponding to the dof axis of the scv the svf belongs to)
+     * \param problem The problem
+     * \param fvGeometry The finite-volume geometry
+     * \param scvf The sub control volume face
+     * \param elemVolVars The volume variables for the element
+     * \param tangentialVelocityDeriv Pre-calculated velocity derivative
+     */
+    template<class Problem, class FVElementGeometry, class ElementVolumeVariables, class Scalar>
+    static auto velocity(const Problem& problem,
+                           const FVElementGeometry& fvGeometry,
+                           const typename FVElementGeometry::SubControlVolumeFace& scvf,
+                           const ElementVolumeVariables& elemVolVars,
+                           Scalar tangentialVelocityDeriv)
+   {
+        assert(scvf.isLateral());
+        assert(scvf.boundary());
+
+        static constexpr int dimWorld = FVElementGeometry::GridGeometry::GridView::dimensionworld;
+        using Vector = Dune::FieldVector<Scalar, dimWorld>;
+
+        Vector porousMediumVelocity(0.0);
+
+        if constexpr (slipCondition == NavierStokes::SlipConditions::bj)
+            porousMediumVelocity = problem.porousMediumVelocity(fvGeometry, scvf);
+        else if (!(slipCondition == NavierStokes::SlipConditions::bjs))
+            DUNE_THROW(Dune::NotImplemented, "Fcstaggered currently only implements BJ or BJS slip conditions");
+
+        const auto& scv = fvGeometry.scv(scvf.insideScvIdx());
+
+        // create a unit normal vector oriented in positive coordinate direction
+        Vector tangent(0.0);
+        tangent[scv.dofAxis()] = 1.0;
+
+        // du/dy + dv/dx = beta * (u_boundary-uPM)
+        // beta = alpha/sqrt(K)
+        const Scalar betaBJ = problem.betaBJ(fvGeometry, scvf, tangent);
+        const Scalar distanceNormalToBoundary = (scvf.ipGlobal() - scv.dofPosition()).two_norm();
+
+        static const bool onlyNormalGradient = getParamFromGroup<bool>(problem.paramGroup(), "FreeFlow.EnableUnsymmetrizedVelocityGradientForBeaversJoseph", false);
+        if (onlyNormalGradient)
+            tangentialVelocityDeriv = 0.0;
+
+        const Scalar scalarSlipVelocity = (tangentialVelocityDeriv*distanceNormalToBoundary
+            + porousMediumVelocity * tangent * betaBJ * distanceNormalToBoundary
+            + elemVolVars[scv].velocity()) / (betaBJ*distanceNormalToBoundary + 1.0);
+
+        return scalarSlipVelocity*tangent;
+    }
+};
+
+} // end namespace Dumux
+
+#endif
diff --git a/test/multidomain/boundary/freeflowporenetwork/1p_1p/problem_freeflow.hh b/test/multidomain/boundary/freeflowporenetwork/1p_1p/problem_freeflow.hh
index 14005f17791440f4ad9e12c1e7112631a6d95f75..0c2cde830521373443d436259c25a29442c57ccc 100644
--- a/test/multidomain/boundary/freeflowporenetwork/1p_1p/problem_freeflow.hh
+++ b/test/multidomain/boundary/freeflowporenetwork/1p_1p/problem_freeflow.hh
@@ -166,7 +166,8 @@ public:
     {
         BoundaryFluxes values(0.0);
         const auto& globalPos = scvf.ipGlobal();
-        using FluxHelper = NavierStokesMomentumBoundaryFluxHelper;
+        using SlipVelocityPolicy = NavierStokesSlipVelocity<typename GridGeometry::DiscretizationMethod,  NavierStokes::SlipConditions::BJ>;
+        using FluxHelper = NavierStokesMomentumBoundaryFlux<typename GridGeometry::DiscretizationMethod, SlipVelocityPolicy>;
 
         if constexpr (ParentType::isMomentumProblem())
         {
diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh
index be65c269cf22a0d86921b831fa1fe32a4f87a0e1..3b3912ef1154cec99c744bc6374495e670d755af 100644
--- a/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh
+++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/convergence/problem_freeflow.hh
@@ -164,7 +164,8 @@ public:
     {
         BoundaryFluxes values(0.0);
         const auto& globalPos = scvf.ipGlobal();
-        using FluxHelper = NavierStokesMomentumBoundaryFluxHelper;
+        using SlipVelocityPolicy = NavierStokesSlipVelocity<typename GridGeometry::DiscretizationMethod, NavierStokes::SlipConditions::BJS>;
+        using FluxHelper = NavierStokesMomentumBoundaryFlux<typename GridGeometry::DiscretizationMethod, SlipVelocityPolicy>;
 
         // momentum boundary conditions
         if constexpr (ParentType::isMomentumProblem())
@@ -283,18 +284,16 @@ public:
     { return InitialValues(0.0); }
 
     /*!
-     * \brief Returns the intrinsic permeability of required as input parameter
-              for the Beavers-Joseph-Saffman boundary condition
+     * \brief Returns the beta value which is the alpha value divided by the square root of the (scalar-valued) interface permeability.
      */
-    auto permeability(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
-    { return couplingManager().darcyPermeability(fvGeometry, scvf); }
-
-    /*!
-     * \brief Returns the alpha value required as input parameter for the
-              Beavers-Joseph-Saffman boundary condition.
-     */
-    Scalar alphaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
-    { return couplingManager().problem(CouplingManager::porousMediumIndex).spatialParams().beaversJosephCoeffAtPos(scvf.ipGlobal()); }
+    Scalar betaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, const GlobalPosition& tangentialVector) const
+    {
+        const auto& K = couplingManager().darcyPermeability(fvGeometry, scvf);
+        const auto alphaBJ = couplingManager().problem(CouplingManager::porousMediumIndex).spatialParams().beaversJosephCoeffAtPos(scvf.ipGlobal());
+        const auto interfacePermeability = vtmv(tangentialVector, K, tangentialVector);
+        using std::sqrt;
+        return alphaBJ / sqrt(interfacePermeability);
+    }
 
     /*!
      * \brief Returns the analytical solution of the problem at a given position.
diff --git a/test/multidomain/boundary/freeflowporousmedium/1p_1p/problem_freeflow.hh b/test/multidomain/boundary/freeflowporousmedium/1p_1p/problem_freeflow.hh
index f1a8da197408ab30c22f2bd9c3f039f7d5d4368b..a11c9c939711d4344f928f861ef287d24f5a7463 100644
--- a/test/multidomain/boundary/freeflowporousmedium/1p_1p/problem_freeflow.hh
+++ b/test/multidomain/boundary/freeflowporousmedium/1p_1p/problem_freeflow.hh
@@ -180,7 +180,8 @@ public:
     {
         BoundaryFluxes values(0.0);
         const auto& globalPos = scvf.ipGlobal();
-        using FluxHelper = NavierStokesMomentumBoundaryFluxHelper;
+        using SlipVelocityPolicy = NavierStokesSlipVelocity<typename GridGeometry::DiscretizationMethod, NavierStokes::SlipConditions::BJS>;
+        using FluxHelper = NavierStokesMomentumBoundaryFlux<typename GridGeometry::DiscretizationMethod, SlipVelocityPolicy>;
 
         if constexpr (ParentType::isMomentumProblem())
         {
@@ -236,16 +237,16 @@ public:
     }
 
     /*!
-     * \brief Returns the intrinsic permeability of required as input parameter for the Beavers-Joseph-Saffman boundary condition
+     * \brief Returns the beta value which is the alpha value divided by the square root of the (scalar-valued) interface permeability.
      */
-    Scalar permeability(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
-    { return couplingManager_->darcyPermeability(fvGeometry, scvf); }
-
-    /*!
-     * \brief Returns the alpha value required as input parameter for the Beavers-Joseph-Saffman boundary condition
-     */
-    Scalar alphaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) const
-    { return couplingManager_->problem(CouplingManager::porousMediumIndex).spatialParams().beaversJosephCoeffAtPos(scvf.ipGlobal()); }
+    Scalar betaBJ(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf, const GlobalPosition& tangentialVector) const
+    {
+        const auto& K = couplingManager_->darcyPermeability(fvGeometry, scvf);
+        const auto alphaBJ = couplingManager_->problem(CouplingManager::porousMediumIndex).spatialParams().beaversJosephCoeffAtPos(scvf.ipGlobal());
+        const auto interfacePermeability = vtmv(tangentialVector, K, tangentialVector);
+        using std::sqrt;
+        return alphaBJ / sqrt(interfacePermeability);
+    }
 
     /*!
      * \brief Returns the pressure difference between inlet and outlet for the horizontal flow scenario